抽象类,接口,内部类
1. 引用数据类型的转换
-
在强制类型转换之前应该使用instanceof进行判断后再进行强制类型转换
if(sr instanceof Circle){ System.out.println("可以转换"); }else{ System.out.println("摆烂吧"); }
2. 抽象的概念
- 抽象方法:
- 抽象主要指不能具体实现的方法并且使用abstract关键字修饰,也就是没有方法体
- 具体格式如下: 访问权限abstract返回值类型 方法名(形参列表);
- 抽象类:
- 指的是不能具体实例化的类并且使用abstract关键字修饰,也就是说不可以创建对象。
- 抽象类中可以有成员变量,成员方法。
- 抽象类中可以有抽象方法也可以没有抽象方法。
- 拥有抽象方法的类一定是抽象类,因此真正意义上的抽象类应该是具有抽象方法并且使用abstract修饰的类。
- 抽象类的意义:
- 抽象类的实际意义不在与被创建对象而在于被继承。
- 开发经验:
- 在实际的开发过程中,推荐使用多态的方式,此时父类类型引用直接调用的方法一定是父类中拥有的方法,若更换子类的时候,只需要将new关键字后面的子类类型修改而无需求改就可以立即生效,从而提高了代码的可维护性和可扩展性。
- 缺点:父类不可以调用子类独有的方法,如果需要调用子类独有的方法就需要强制类型转换。
- 一些提示:
- private不可以和abstract共同修饰方法,因为private的方法无法被继承,显然违背了abstract的用意
- final不可以和abstract共同修饰一个关键字
- static关键字不可以和abstract方法共同修饰一个方法。因为static关键字属于类层级,如果可以共同修饰的话那就可以直接用类名直接调用abstract修饰的方法,这就又让abstract修饰变得没有意义,并且该方法没有方法体。所以这样做没意义。
3. 接口
-
概念: 接口就是一种比抽象类还抽象的类,体现在所有方法都是抽象方法
-
定义类的关键字是class,而定义接口的关键字为interface
-
接口里面只能有常量也就是由public static final共同修饰。
-
从jdk1.9开始允许接口中出现私有方法
-
一个小案例:
定义一个hunter接口和runner接口,hunter接口继承runner接口,hunter接口里面实现一个捕猎的抽象方法,runner接口里面实现一个奔跑的抽象方法。定义一个man类实现hunter接口和Runner接口。运用多态的方式调用这俩个方法。
package com.lagou.jiekou; public interface Runner {//runner接口 public void run(); } ====================================================== package com.lagou.jiekou; public interface Hunter extends Runner {//Hunter接口 void Hunter_(); } ====================================================== package com.lagou.jiekou; public class Man implements Hunter {//实现了这俩个接口的man类(这里因为hunter继承了runner所以相当于同时需要实现俩个接口的抽象方法。 @Override public void Hunter_() { System.out.println("正在捕猎"); } @Override public void run() { System.out.println("正在奔跑"); } public static void main(String[] args) { Hunter ht =new Man(); ht.Hunter_(); ht.run(); } }
-
抽象类和接口的主要区别:
- 定义抽象类的关键字是abstract class 而定义接口的关键字是interface
- 继承抽象类的关键字是extends而实现接口的关键字是implements
- 继承抽象类支持单继承,而实现接口支持多实现
- 抽象类中可以有构造方法,而接口中不可以有构造方法。
- 抽象类中可以有成员变量,但是接口中只能有常量。
- 抽象类中只可以有成员方法,但是接口中只能有抽象方法。
- 抽象类中增加方法的时候子类可以不用重写,而接口中增加方法时实现类需要重写。
- Java8之后,在接口的方法中加一个default关键字可以在接口中实现一个默认的方法,这个方法可以由实现类自己选择是否重写。
- Java9之后出现了一个新特性,即接口中可以出现私有方法。
4. 内部类
-
实际作用:
当一个类存在的价值仅仅只是为了某一个类单独服务的时候就可以将这个类定义为所服务的类中的内部类,这样可以颖仓该类的实现细节并且可以方便的访问外部类的私有成员而不再需要提供公有的get和set方法。
-
内部类的分类:
-
普通内部类:
直接将一个类的定义放在另一个类的类体中\
-
和普通类一样可以定义成员变量,成员方法以及构造方法
-
普通内部类和普通类一样可以使用final或者abstract关键字修饰
-
普通内部类还可以使用private或者protected关键字修饰
-
普通内部类需要使用外部类对象来创建对象
public class NormalOuterTest { public static void main(String[] args) { // 声明NormalOuter类型的引用指向该类型的对象 NormalOuter normalOuter = new NormalOuter(); // 声明NormalOuter类中内部类的引用和指向内部类的对象 NormalOuter.NormalInner ni = normalOuter.new NormalInner(); // 调用内部类的show方法 ni.show(); ni.show2(4); } }
-
如果内部类访问外部类中与本类内部类同名的成员变量或者方法时,要使用this关键字
下面是一个内部类访问外部同名变量的例子:
package com.lagou.inclass; /** * 编程实现普通内部类的定义和使用 */ public class NormalOuter { private int cnt=1; // 定义一个普通内部类 public class NormalInner{ private int ia=2; private int cnt = 3; public NormalInner(){ System.out.println("普通内部类的构造方法体"); } public void show(){ System.out.println("外部类中的变量cnt的数值为:"+cnt); System.out.println("ia="+ia); } public void show2(int cnt){ System.out.println("形参变量cnt="+cnt); System.out.println("内部类中的Cnt为:"+this.cnt);//3 //注意这个语句 System.out.println("外部类中的Cnt为:"+NormalOuter.this.cnt);//1 } } }
-
-
静态内部类:
使用static关键字修饰的内部类,隶属于类层级。
- 静态内部类不能直接访问外部类的非静态成员
- 静态内部类可以直接创建对象。
package com.lagou.inclass; public class staticOuter { private int cnt=1; private static int snt=1; /** * 定义静态内部类 */ public static class StaticInner{ private int ia=3; private static int snt=4; public StaticInner(){ System.out.println("静态内部类的构造方法哦!"); } public void show(){ System.out.println("ia= "+ia); System.out.println("外部类的snt = "+snt);//2 } public void show2(int snt){//就近原则 System.out.println("snt="+snt); System.out.println("内部类中的成员snt"+StaticInner.snt);//4 System.out.println("外部类中的成员snt"+staticOuter.snt);//2 } } }
-
局部内部类:
定义在方法体中的类叫做局部内部类,只要注意他的有效范围就行了,别的都和普通的类一样。如果要访问内部类的对象,必须先创建外部类对象,再用外部类对象调用包含内部类的方法。
package com.lagou.jubuClass; /** * 编程实现局部内部类的定义和使用 */ public class AreaOuter { private int cnt =1; public void show(){ // 定义局部内部类,旨在当前方法体的内部好使 class AreaInner{ private int ia = 1; public AreaInner(){ System.out.println("局部内部类的构造方法!"); } public void test(){ System.out.println("ia="+ia); System.out.println("cnt="+cnt); } } AreaInner areaInner = new AreaInner(); areaInner.test(); } }
package com.lagou.jubuClass; import java.awt.geom.Area; public class AreaOuterTest { public static void main(String[] args) { AreaOuter areaOuter = new AreaOuter(); areaOuter.show(); } }
-
局部内部类只能再该方法体内部使用
-
局部内部类可以在方法体内部直接创建对象
-
局部内部类不能使用访问控制符和static关键字修饰符(因为局部变量是不能用这些关键字修饰的)
-
局部内部类可以使用外部方法的局部变量,但是必须是final修饰的,由局部内部类和局部变量的生命周期不同所导致。
-
为什么说生命周期不同:
比如内部类拿到ic的时候,会先把ic拷贝一份,然后使用这个值,如果ic可以随意改变的话。内部类拿到的值可能随时都会和外部的ic不一致。如果不加final修饰会导致数据不一致性的问题。因为内部类的生命周期显然比ic短。他对ic的作用效果也比ic先结束。
如果在内部类中调用了外部方法的局部变量。外部方法的局部变量默认是使用final修饰的。也就是说这个局部变量无法被修改。(虽然可以省略final但是最好还是加上)
![报错信息](https://img-blog.csdnimg.cn/0a53b20a47af465e858d6b6dbf3eea3f.png#pic_center)
这里报错是因为ic默认为final修饰的。
-
-
-
匿名内部类:(需要先了解一下回调模式,就是下一个知识点)
就是指没有名字的内部类。
-
语法规则:(重点)
-
接口/父类类型 引用变量名 = new 接口/父类类型(){方法重写}
AnonymousInterfaceTest.test(new AnonymousInterface() { @Override public void show() { System.out.println("匿名内部类"); } });
-
java 8之后又提出了一个新特性,即lamda表达式格式为:(参数列表)->{方法体}
AnonymousInterface ait = ()-> System.out.println("lamda表达式"); AnonymousInterfaceTest.test(ait);
-
-
回调模式
-
概念: 如果一个方法的参数是接口类型,则在调用该方法的时候,需要创建并传递一个实现此及接口类型的对象,而该方法在运行时会调用到参数对象中所实现的方法(接口中定义的)
package com.lagou.jubuClass; public class AnonymousInterfaceTest { // 假设已有下面的方法 public static void test(AnonymousInterface ai){//比如这里的参数是接口类型 ai.show();//最终执行的show方法是实现类中的show方法。 } public static void main(String[] args) { AnonymousInterfaceTest.test(new AnonymousInterfaceImplement());//传递的参数是接口的实现类 } }
-