周日我们进一步学习了抽象类,接口,还好好分析了四种权限修饰符的特性。
以上是复习上周的面向对象的内容。
抽象类
- 定义:
我们先从一个例子引出抽象类的定义:比如我说一只动物,你知道我说的是什么具体动物吗?只有看到了具体的动物,你才知道,这是什么动物。 所以说,动物本身并不是一个具体的事物,而是一个抽象的事物。只有真正的猫,狗才是具体的动物。同理,我们也可以推想,不同的动物吃的东西应该是不一样的,所以,我们不应该在动物类中给出具体体现,而是应该给出一个声明即可。所以,在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类。父类当中不给出所有子类共性功能的实现,强制子类根据各自的差异性去重写 - 抽象类的特点:
1.抽象类和抽象方法必须用abstract关键字修饰
抽象类:abstract class 类名{}
抽象方法:public abstract void 方法名();
//抽象方法:只给出方法声明,不给出方法的具体实现
2.抽象类不一定有抽象方法,有抽象方法的类一定是抽象类。可是抽象类中的构造方法不能进行实例化,因此它没有意义。
3.抽象类不能直接实例化,而是需要按照多态的方式,由具体的子类实例化,即抽象类多态。
4.如果一个类中如果有了抽象方法,则此类必须为抽象类。
5.抽象类的子类要门时抽象类,要么 必须重写抽象类中的所有抽象方法。所以说抽象类的成员方法是强制要求子类做的事情。
//定义抽象类Animal写了两个抽象方法eat(),sleep();
public abstract class Animal {
public abstract void eat();
public abstract void sleep();
}
//定义子类Cat继承抽象类Animal
public class Cat extends Animal{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override //重写父抽象类中的所有方法。
public void eat() {
System.out.println("猫吃鱼");
}
@Override
public void sleep() {
System.out.println("猫经常睡觉");
}
public void catcheMouser(){ //定义了一个Cat独有的方法
System.out.println("猫抓老鼠");
}
}
//定义子类Dog继承抽象类Animal
public class Dog extends Animal{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override ////重写父抽象类中的所有方法。
public void eat() {
System.out.println("够吃骨头");
}
@Override
public void sleep() {
System.out.println("狗晚上睡觉");
}
public void lookDoor(){ //定义了一个Dog独有的方法
System.out.println("狗看门");
}
}
//定义测试类test
public class Test4 {
public static void main(String[] args) {
Animal an = new Dog(); //多态
an.sleep();
an.eat();
Dog dog = (Dog) an; //向下转型
dog.setName("旺财");
dog.setAge(5);
System.out.println(dog.getName());
System.out.println(dog.getAge());
dog.lookDoor();
System.out.println("-----------------------");
an = new Cat();
an.eat();
an.sleep();
Cat cat = (Cat) an; //向下转型
cat.setName("小白");
cat.setAge(2);
cat.catcheMouser();
System.out.println(cat.getAge());
System.out.println(cat.getName());
}
}
以上的例子充分展示了抽象类以及多态的使用方法。子类继承抽象父类,并且重写父类中的所有方法,同时定义了自身特有的方法,并通过多态向下转型调用使用。
注意抽象类关键字abstract和一些关键字是冲突的。
a. private 冲突(abstract定义抽象类,它强制要求子类重写方法,而private有将它私有,不许继承和重写,因此冲突)。
b. final 冲突(与private相似)
c. static 不能共存,这样无意义(static 修饰的方法算不上重写)
上面我们讨论了一些修饰符的特点,那我们就来详细讨论一下修饰符:
四种权限修饰符:private(私有) ,默认,protected(受保护的),public(公共的)
- 权限表:
本类 | 同一个包下(子类和无关类) | 不同包下(子类) | 不同包下(无关类) | |
---|---|---|---|---|
private | Y | |||
默认 | Y | Y | ||
protect | Y | Y | Y | |
public | Y | Y | Y | Y |
权限修饰符:private,默认的,protected,public
状态修饰符:static,final
抽象修饰符:abstract
接口
- 概述:
我们继续之前的案例,猫狗除了吃饭睡觉(继承)我们想想狗一般就是看门,猫一般就是作为宠物了(多态,重写)。但是,现在有很多的驯养员或者是驯兽师,可以训练出:猫钻火圈,狗跳高,狗做计算等。而这些额外的动作,并不是所有猫或者狗一开始就具备的,这应该属于经过特殊的培训训练出来的。所以,这些额外的动作定义到动物类中就不合适,也不适合直接定义到猫或者狗中,因为只有部分猫狗具备这些功能。
所以,为了体现事物功能的扩展性,Java中就提供了接口来定义这些额外功能,并不给出具体实现,将来哪些猫狗需要被培训,只需要这部分猫狗把这些额外功能实现即可
接口:定义一些扩展功能或者规范.哪个类想要具备这些扩展功能可以实现这个接口。 - 定义:
关键字:interface
格式:interface 接口名{}
实现:implements class 类名 implements 接口名{} - 特点:
1.接口中的成员变量前面有默认修饰符 public static final 。
2.方法前面默认有修饰符 public abstract 你可以省略不写,建议你写出来
3.接口中所有的方法都是抽象方法,抽象类中可以有抽象,也可以有非抽象方法
4.接口中没有构造方法
5.接口中所有的成员变量都是公共的静态常量
6.接口的子类可以是抽象类
7.接口跟接口之间可以继承,而且是多继承
8.接口不能创建对象,即接口不能实例化,如果想实例化要按照多态的方式来实例化。
9.接口可以实现多继承,而default关键字可以允许在接口中定义默认类,这也相当于变相承认java多继承。
接口实例:
//定义抽象父类Animal
public abstract class Animal {
public abstract void eat();
public abstract void sleep();
}
//定义接口MyInterface
public interface MyInterface {
int NUM = 100;
(public static final) double D = 100; //括号里的可以没有,默认修饰符。
(public abstract) void fire();
void hehe();
}
//implements 实现 用来实现一个接口的
//定义子类Dog,继承抽象父类Animal,同时调用接口MyInterface
public class Dog extends Animal implements MyInterface{
//先重写抽象父类中的方法eat(),sleep().
@Override
public void eat() {
}
@Override
public void sleep() {
}
//实例化接口的方法fire(),jump().
@Override
public void fire() {
System.out.println("狗钻火圈了");
}
@Override
public void jump() {
System.out.println("狗跳高了");
}
}
//test测试类
public class Test {
public static void main(String[] args) {
//接口不能实例化
MyInterface my = new Dog(); //多态的形式
my.fire();
my.jump();
}
}
-
!参数传递:
下面我们来说说java中的参数传递。
1.如果一个方法的形参要一个 类 类型,则传一个该类的对象。
2.如果一个方法的形参要一个 抽象类类型 那么你就传该类的一个子类对象 。
3.如果一个方法的返回值类型要一个 接口 类型 你就返回一个该接口的子类对象。
4.如果一个方法的返回值类型 要一个类 类型,你就返回一个该类的对象。
这里参数的传递给了我们一个很好的编程方法,链式编程(连调),这样可以简化编程的复杂性,但是需要我们对参数的传递有深刻的理解。 -
内部类:
1.定义:
将一个类 定义到另一个类的内部,我们就称之为内部类;将类A 定义到B类当中 我们称A为内部类,B 我们可以叫外部类;同时根据定义位置的不同,我们分为成员内部类,局部内部类;
成员内部类:内部类在外部类的成员位置。
局部内部类:内部类在外部类的局部位置(方法中)。
2.内部类的特点:
(1):内部类可以直接访问外部类的成员,包括私有的;
(2):内部类被private 修饰后,外界不能创建内部类对象;
(3):static 可以修饰内部类,同时静态内部类只能访问外部类的静态成员;
(4):局部内部类访问局部变量必须用final修饰 在JDK1 .8之后默认就加上了;
因为局部变量会随着方法的调用完毕而消失,这个时候,局部对象并没有立马从堆内存中消失,还要使用那个变量。
为了让数据还能继续被使用,就用fianl修饰,这样,在堆内存里面存储的其实是一个常量值。
public class Outer {
int num=100;
private String name="abc";
public void waiMethod(){
//定义局部内部类
//内部类可以直接访问外部类的成员 包括私有的
class Inner{
int nei=10;
public void neiShow(){
System.out.println(num);
System.out.println(name);
}
public void neiTest(){
waiTest();
}
}
Inner inner = new Inner();
System.out.println(inner.nei);
inner.neiShow();
inner.neiTest();
}
private void waiTest(){
System.out.println("我是外部类的私有方法");
}
}
public class Test5 {
public static void main(String[] args) {
//局部内部类,外界无法直接创建其对象
new Outer().waiMethod();
}
}