抽象类: abstract
abstract类由来
父类中的方法,被它的子类们重写,子类各自的实现都不尽相同。那么父类的方法声明和方法主体,只有声明还有意义,而方法主体则没有存在的意义了。我们把没有方法主体的方法称为抽象方法。
一个abstract类只关心它的子类是否具有某种功能,并不关心其自身功能的具体行为,功能的具体行为由子类负责实现。抽象类不能被直接实例化,即不能使用关键字new来创建该抽象类的对象。
抽象:不具体,看不明白。抽象类表象体现。在不断抽取过程中,将共性内容中的方法声明抽取,但是方法不一样,没有抽取,这时抽取到的方法,并不具体,需要被指定关键字abstract所标示,声明为抽象方法。
抽象方法所在类一定要标示为抽象类,也就是说该类需要被abstract关键字所修饰。
abstract类定义
Java语法规定,包含抽象方法的类就是抽象类。
声明方法的存在而不去实现它。abstract修饰的类称为做抽象类;abstract修饰的方法叫做抽象方法,抽象方法只有声明部分,而没有具体的方法体。
abstract方法
使用 abstract 关键字修饰方法,该方法就成了抽象方法,抽象方法只包含一个方法名,而没有方法体。
定义格式:修饰符 abstract 返回值类型 方法名 (参数列表);
代码举例:public abstract void run();
abstract类的特点:
1:抽象方法只能定义在抽象类中,抽象类和抽象方法必须由abstract关键字修饰(可以描述类和方法,不可以描述变量)。
2:抽象方法只定义方法声明,并不定义方法实现。
3:抽象类不可以被创建对象(实例化)。
4:只有通过子类继承抽象类并覆盖了抽象类中的所有抽象方法后,该子类才可以实例化。否则,该子类还是一个抽象类。
abstract类的细节:
1:抽象类中是否有构造方法?有,用于给子类对象进行初始化。
2:抽象类中是否可以定义非抽象方法?
可以。其实,抽象类和一般类没有太大的区别,都是在描述事物,只不过抽象类在描述事物时,有些功能不具体。所以抽象类和一般类在定义上,都是需要定义属性和行为的。只不过,比一般类多了一个抽象方法。而且比一般类少了一个创建对象的部分。
3:抽象关键字abstract和哪些不可以共存?final , private , static定义抽象方法的目的是重写此方法,但如果定义成静态方法就不能被重写
4:抽象类中可不可以不定义抽象方法?可以。抽象方法目的仅仅为了不让该类创建对象。
abstract类的使用规则
抽象类中可以没有abstract方法(为了强迫使用者必须通过继承来使用这个类);但是一旦类中包含了abstract方法,则这个“类”一定是abstract类,即有抽象方法的类一定是抽象类。
抽象类的子类必须实现抽象类中的所有抽象方法,否则子类也必须是抽象类。最终,必须有子类实现该父类的抽象方法,否则,从最初的父类到最终的子类都不能创建对象,失去意义。
public class Cat extends Animal { public void run (){ System.out.println("小猫在墙头走~~~"); } } 输出结果: 小猫在墙头走~~~ | public class CatTest { public static void main(String[] args) { // 创建子类对象 Cat c = new Cat(); // 调用run方法 c.run(); } } |
此时的方法重写,是子类对父类抽象方法的完成实现,我们将这种方法重写的操作,也叫做实现方法。
abstract类与普通类
抽象类前面由abstract修饰,而普通类没有。
抽象类可以像普通类一样拥有构造方法、静态或非静态成员变量、静态或非静态代码块和静态非抽象方法或非静态非抽象方法;但抽象类不能创建对象。
1、abstract final class Mammal{ } 能编译通过吗, why?
原因:如果抽象类前面可以添加final就意味着该类无法被继承,也就意味着该抽象类中的抽象方法永远无法得到实现,也就意味着抽象类中的抽象方法是无用的。
2、Mammal抽象类中move抽象方法的访问权限可以为private吗,即“private abstract void move();”, why?
原因:被private修饰的方法其作用范围为本类,如果抽象类中的抽象方法被private修饰就意味着该方法永远无法被实现。
3、Mammal抽象类中move抽象方法可以由static修饰吗,,即“public static abstract void move();” why?
原因:抽象类中的抽象方法如果可以被static修饰就意味着可以使用抽象类的类名来使用该方法,但是该抽象方法没有方法体,不具有使用的价值,所以Java中规定抽象类中不能包含被static修饰的静态抽象方法。
4、抽象类中的抽象方法是多态的一种表现形式。
---------------------------------------------------------------------
abstract类模板方法设计模式:
解决的问题:当功能内部一部分实现时确定,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
abstract class GetTime{ public final void getTime(){ //此功能如果不需要复写,可加final限定 long start = System.currentTimeMillis(); code(); //不确定的功能部分,提取出来,通过抽象方法实现 long end = System.currentTimeMillis(); System.out.println("毫秒是:"+(end-start)); } public abstract void code(); //抽象不确定的功能,让子类复写实现 } | class SubDemo extends GetTime{ public void code(){ //子类复写功能方法 for(int y=0; y<1000; y++){ System.out.println("y"); } } } |
abstract类注意事项
关于抽象类的使用,以下为语法上要注意的细节,虽然条目较多,但若理解了抽象的本质,无需死记硬背。
1. 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。
2. 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。
3. 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。
4. 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。
理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。
接口:interface
接口定义
接口,是Java语言中一种引用类型,是方法的集合,如果说类的内部封装了成员变量、构造方法和成员方法,那么接口的内部主要就是封装了方法,包含抽象方法(JDK 7及以前),默认方法和静态方法(JDK 8),私有方法(JDK 9)。
接口的定义,使用 interface 关键字,会被编译成.class文件,但它并不是类,而是另外一种引用数据类型。
引用数据类型:数组,类,接口。
接口的使用,它不能创建对象,但是可以被实现( implements ,类似于被继承)。一个实现接口的类(可以看做是接口的子类),需要实现接口中所有的抽象方法,创建该类对象,就可以调用方法了,否则它必须是一个抽象类。
面向接口编程,传递依赖关系有三种方式:接口传递、构造方法传递、setter传递。在实际的开发中我们主要通过以下几点来较好的遵守依赖倒转原则:
底层模块尽量都要有抽象类或接口,或两者都有
变量的声明类型尽量是抽象类或接口
使用继承时要遵循里式替换原则
遵循依赖倒转原则可以降低类之间的耦合性,提高系统稳定性,降低修改造成的风险。
接口格式
public interface 接口名称 {
// 抽象方法
// 默认方法
// 静态方法
// 私有方法
}
接口含有全局常量。
注意:接口中的成员都有固定的修饰符。
成员变量:public static final
interface Inter{
public static final int x = 3;
}
接口含有抽象方法
抽象方法:使用 abstract 关键字修饰,可以省略,没有方法体。该方法供子类实现使用。接口中有抽象方法,说明接口不可以实例化。接口的子类必须实现了接口中所有的抽象方法后,该子类才可以实例化。否则,该子类还是一个抽象类。
代码如下:
public interface InterFaceName {
public abstract void method();
}
接口含有默认方法和静态方法
默认方法:使用 default 修饰,不可省略,供子类调用或者子类重写。
静态方法:使用 static 修饰,供接口直接调用。
代码如下:
public interface InterFaceName {
public default void method() {
// 执行语句
}
public static void method2() {
// 执行语句
}
}
接口含有私有方法和私有静态方法
私有方法:使用 private 修饰,供接口中的默认方法或者静态方法调用。
代码如下:
public interface InterFaceName {
private void method() {
// 执行语句
}
}
接口基本的实现
接口实现的概述
类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。实现的动作类似继承,格式相仿,只是关键字不同,实现使用 implements 关键字。
非抽象子类实现接口:
1. 必须重写接口中所有抽象方法。
2. 继承了接口的默认方法,即可以直接调用,也可以重写。
实现格式:
class 类名 implements 接口名 {
// 重写接口中抽象方法【必须】
// 重写接口中默认方法【可选】
}
接口中抽象方法的使用
必须全部实现,代码如下:
定义接口: public interface LiveAble { // 定义抽象方法 public abstract void eat(); public abstract void sleep(); } 输出结果: 吃东西 晚上睡 | 定义实现类: public class Animal implements LiveAble { @Override public void eat() { System.out.println("吃东西"); } @Override public void sleep() { System.out.println("晚上睡"); } } | 定义测试类: public class InterfaceDemo { public static void main(String[] args) { // 创建子类对象 Animal a = new Animal(); // 调用实现后的方法 a.eat(); a.sleep(); } } |
接口中默认方法的使用
可以继承,可以重写,二选一,但是只能通过实现类的对象来调用。
- 继承默认方法,代码如下:
定义接口 public interface LiveAble { public default void fly(){ System.out.println("天上飞"); } } 定义实现类: public class Animal implements LiveAble { // 继承,什么都不用写,直接调用 } | 定义测试类: public class InterfaceDemo { public static void main(String[] args) { // 创建子类对象 Animal a = new Animal(); // 调用默认方法 a.fly(); } } 输出结果: 天上飞 |
- 重写默认方法,代码如下:
定义接口: public interface LiveAble { public default void fly(){ System.out.println("天上飞"); } } 定义实现类: public class Animal implements LiveAble { @Override public void fly() { System.out.println("自由自在的飞"); } } | 定义测试类: public class InterfaceDemo { public static void main(String[] args) { // 创建子类对象 Animal a = new Animal(); // 调用重写方法 a.fly(); } } 输出结果: 自由自在的飞 |
接口中静态方法的使用
静态与.class 文件相关,只能使用接口名调用,不可以通过实现类的类名或者实现类的对象调用,代码如下:
定义接口: public interface LiveAble { public static void run(){ System.out.println("跑起来~~~"); } } 定义实现类 public class Animal implements LiveAble { // 无法重写静态方法 } | 定义测试类: public class InterfaceDemo { public static void main(String[] args) { // Animal.run(); // 【错误】无法继承方法,也无法调用 LiveAble.run(); // } } 输出结果: 跑起来~~~ |
接口中私有方法的使用
私有方法:只有默认方法可以调用。
私有静态方法:默认方法和静态方法可以调用。
如果一个接口中有多个默认方法,并且方法中有重复的内容,那么可以抽取出来,封装到私有方法中,供默认方法去调用。从设计的角度讲,私有的方法是对默认方法和静态方法的辅助。同学们在已学技术的基础上,可以自行测试。
定义接口:
public interface LiveAble {
default void func(){
func1();
func2();
}
private void func1(){
System.out.println("跑起来~~~");
}
private void func2(){
System.out.println("跑起来~~~");
}
}
接口的多实现
在继承体系中,一个类只能继承一个父类。而对于接口而言,一个类是可以实现多个接口的,这叫做接口的多实现。并且,一个类能继承一个父类,同时实现多个接口。
实现格式
class 类名 [extends 父类名] implements 接口名1,接口名2,接口名3... {
// 重写接口中抽象方法【必须】
// 重写接口中默认方法【不重名时可选】
}
抽象方法
接口中,有多个抽象方法时,实现类必须重写所有抽象方法。如果抽象方法有重名的,只需要重写一次。代码如下:
定义多个接口: interface A { public abstract void showA(); public abstract void show(); } interface B { public abstract void showB(); public abstract void show(); } | 定义实现类: public class C implements A,B{ @Override public void showA() { System.out.println("showA"); } @Override public void showB() { System.out.println("showB"); } @Override public void show() { System.out.println("show"); } } |
默认方法
接口中,有多个默认方法时,实现类都可继承使用。如果默认方法有重名的,必须重写一次。代码如下:
定义多个接口: interface A { public default void methodA(){} public default void method(){} } interface B { public default void methodB(){} public default void method(){} } | 定义实现类: public class C implements A,B{ @Override public void method() { System.out.println("method"); } } |
静态方法
接口中,存在同名的静态方法并不会冲突,原因是只能通过各自接口名访问静态方法。
优先级的问题
` 当一个类,既继承一个父类,又实现若干个接口时,父类中的成员方法与接口中的默认方法重名,子类就近选择执行父类的成员方法。代码如下:
定义接口: interface A { public default void methodA(){ System.out.println("AA"); } } | 定义父类: class D { public void methodA(){ System.out.println("DDD"); } 定义子类: class C extends D implements A { // 未重写methodA方法 } | 定义测试类: public class Test { public static void main(String[] args) { C c = new C(); c.methodA(); } } 输出结果: |
接口的多继承
一个接口能继承另一个或者多个接口,这和类之间的继承比较相似。接口的继承使用 extends 关键字,子接口继承父接口的方法。如果父接口中的默认方法有重名的,那么子接口需要重写一次。代码如下:
定义父接口: interface A { public default void method(){ System.out.println("AAAAAAAAAAAAAAAAAAA"); } } interface B { public default void method(){ System.out.println("BBBBBBBBBBBBBBBBBBB"); } } | 定义子接口: interface D extends A,B{ @Override public default void method() { System.out.println("DDDDDDDDDDDDDD"); } } 小贴士: 子接口重写默认方法时,default关键字可以保留。 子类重写默认方法时,default关键字不可以保留。 |
接口中其他成员特点
- 接口中,无法定义成员变量,但是可以定义常量,其值不可以改变,默认使用public static final修饰。
- 接口中,没有构造方法,不能创建对象。
- 接口中,没有静态代码块。
接口回调
抽接口回调描述的是一种现象:接口声明的变量指向其实现类实例化的对象,那么该接口变量就可以调用其实现类中的方法。
接口与类的区别
类与类之间存在着继承关系,类与接口中间存在的是实现关系。
继承用extends ;实现用implements ;
通过extends关键字可以使当前所自定义的接口实现继承,但需要注意以下两点:
1、接口只能继承父接口,不能继承抽象类和普通类。
2、接口弥补了Java单一继承的缺点(Java中的类只能继承一个父类),即接口可以继承多个父接口,它们之间用英文逗号隔开。
接口和类不一样的地方,就是,接口可以被多实现,这就是多继承改良后的结果。java将多继承机制通过多现实来体现。
一个类在继承另一个类的同时,还可以实现多个接口。所以接口的出现避免了单继承的局限性。还可以将类进行功能的扩展。
其实java中是有多继承的。接口与接口之间存在着继承关系,接口可以多继承接口。
接口都用于设计上,设计上的特点:(可以理解主板上提供的接口)
1:接口是对外提供的规则。
2:接口是功能的扩展。
3:接口的出现降低了耦合性。
接口与抽象类:
抽象类:一般用于描述一个体系单元,将一组共性内容进行抽取,特点:可以在类中定义抽象内容让子类实现,可以定义非抽象内容让子类直接使用。它里面定义的都是一些体系中的基本内容。
接口:一般用于定义对象的扩展功能,是在继承之外还需这个对象具备的一些功能。
抽象类和接口的共性:
都是不断向上抽取的结果。
抽象类和接口的区别:
1:抽象类只能被继承,而且只能单继承。接口需要被实现,而且可以多实现。
2:抽象类中可以定义非抽象方法,子类可以直接继承使用。接口中都有抽象方法,需要子类去实现。
3:抽象类使用的是is a 关系。接口使用的 like a 关系。
4:抽象类的成员修饰符可以自定义。接口中的成员修饰符是固定的。全都是public的。
在开发之前,先定义规则,A和B分别开发,A负责实现这个规则,B负责使用这个规则。至于A是如何对规则具体实现的,B是不需要知道的。这样这个接口的出现就降低了A和B直接耦合性。
内部类:
内部类是指在一个外部类内定义的类,他对于简化事件处理非常有用。按照是否有类名分为有名内部类和匿名内部类。
外部类可以调用内部类的静态成员,非静态成员,但内部类只能访问外部类的非静态成员
内部类静态内部类跟非静态内部类:
非静态内部类需要外部类实例化之后才能实例化。
内部类的特点:
1、普通外部类、接口和抽象类只能使用public和default修饰,内部类四种访问权限修饰符都可以;
2、static不能修饰普通外部类、接口和抽象类,但可以修饰内部类;
3、内部类是一个独立的类:编译之后内部类会被编译成独立的.class文件,文件名为外部类的类名+$+数字;
4、内部类有static修饰,则内部类只能“直接”访问外部类的静态成员变量;
5、普通外部类、接口和抽象类都可以成为匿名内部类;
6、内部类可以访问外部类的所有访问权限的成员变量;
7,内部类不允许声明static成员。
接口内部类:
|
|
如果A类需要直接访问B类中的成员,而B类又需要建立A类的对象。这时,为了方便设计和访问,直接将A类定义在B类中。就可以了。A类就称为内部类。内部类可以直接访问外部类中的成员。而外部类想要访问内部类,必须要建立内部类的对象。
class Outer{
int num = 4;
class Inner {
void show(){
System.out.println("inner show run "+num);
}
}
public void method(){
Inner in = new Inner();//创建内部类的对象。
in.show();//调用内部类的方法。
}
}
当内部类定义在外部类中的成员位置上,可以使用一些成员修饰符修饰 private、static。
1:默认修饰符。
直接访问内部类格式:外部类名.内部类名 变量名 = 外部类对象.内部类对象;
Outer.Inner in = new Outer.new Inner();//这种形式很少用。
但是这种应用不多见,因为内部类之所以定义在内部就是为了封装。想要获取内部类对象通常都通过外部类的方法来获取。这样可以对内部类对象进行控制。
2:私有修饰符。
通常内部类被封装,都会被私有化,因为封装性不让其他程序直接访问。
3:静态修饰符。
如果内部类被静态修饰,相当于外部类,会出现访问局限性,只能访问外部类中的静态成员。
注意;如果内部类中定义了静态成员,那么该内部类必须是静态的。
内部类编译后的文件名为:
“外部类名$内部类名.java”;
内部类为什么可以直接访问外部类中的成员呢?
那是因为内部中都持有一个外部类的引用。这个是引用是 外部类名.this
内部类可以定义在外部类中的成员位置上,也可以定义在外部类中的局部位置上。
当内部类被定义在局部位置上,只能访问局部中被final修饰的局部变量。
匿名内部类:
没有名字的内部类。就是内部类的简化形式。一般只用一次就可以用这种形式。匿名内部类其实就是一个匿名子类对象。想要定义匿名内部类:需要前提,内部类必须继承一个类或者实现接口。
匿名内部类的格式:
new 父类名&接口名(){ 定义子类成员或者覆盖父类方法 }.方法。
匿名内部类的使用场景:
当方法的参数是接口类型引用时,如果接口中的方法不超过3个。可以通过匿名内部类来完成参数的传递。
其实就是在创建匿名内部类时,该类中的封装的方法不要过多,最好两个或者两个以内。
new Object(){ void show(){ System.out.println("show run"); } }.show(); | Object obj = new Object(){ void show(){ System.out.println("show run"); } }; obj.show(); |
写法是正确,1和2都是在通过匿名内部类建立一个Object类的子类对象。
区别:第一个可是编译通过,并运行。第二个编译失败,因为匿名内部类是一个子类对象,当用Object的obj引用指向时,就被提升为了Object类型,而编译时检查Object类中是否有show方法,所以编译失败。
class InnerClassDemo6 { +(static)class Inner{ void show(){} } public void method(){ this.new Inner().show();//可以 } public static void main(String[] args) { //static不允许this错误,Inner类需要定义成static This.new Inner().show(); } } interface Inter{ void show(); } | class Outer{ //通过匿名内部类补足Outer类中的代码。 public static Inter method(){ return new Inter(){ public void show(){} }; } } |
class InnerClassDemo7 { public static void main(String[] args) { Outer.method().show(); //Outer.method():意思是:Outer中有一个名称为method的方法,而且这个方法是静态的。 //Outer.method().show():当Outer类调用静态的method方法运算结束后的结果又调用了show方法,意味着:method()方法运算完一个是对象,而且这个对象是Inter类型的。 function (new Inter(){ public void show(){} }); //匿名内部类作为方法的参数进行传递。 } public static void function(Inter in){ in.show(); } } |
普通类:
普通父类的有名内部类 | 普通类的匿名内部类: |
总结:有名内部类可以添加新的属性和方法,匿名内部类无法添加新的属性和方法,因为匿名内部类只存在上传型对象;
抽象类:
抽象类的有名内部类 | 抽象类的匿名内部类 |