1.abstract
问题引入
用前面学习的内容,实现下面案例,需求如下:
定义猫类(Cat)和狗类(Dog)
猫类成员方法:eat:猫吃鱼狗类成员方法:eat:狗吃肉
sleep:躺着睡sleep:趴着睡
根据前面所学基本内容(继承、重写),代码实现如下:
.chap07.test;
//基类
classAnimal{
publicvoideat(){
System.out.println("吃饭,吃的东西不确定...");}
publicvoidsleep(){
System.out.println("睡觉,姿势不确定...");}
}
//子类Cat,重写方法
classCatextendsAnimal{@Override
publicvoideat(){
System.out.println("猫吃鱼");}
@Override
publicvoidsleep(){
System.out.println("躺着睡");
}
}
//子类Dog,重写方法
classDogextendsAnimal{@Override
publicvoideat(){
System.out.println("狗吃肉");
}
@Override
publicvoidsleep(){
System.out.println("趴着睡");
}
}
publicclassTest011_Animal{
publicstaticvoidmain(String[]args){Animala=newAnimal();
a.eat();a.sleep();System.out.println("-------------");
Animalc=newCat();c.eat();
c.sleep();
System.out.println("-------------");
Animald=newDog();d.eat();
d.sleep();
}
}运行效果:
思考,上述代码能够完成相应业务,但是否存在不合理的地方?父类Animal中eat、sleep,这两个方法的功能是不明确的,如何通过代码体现这种不明确
实际开发中,一般不会实例化Animal类对象,主要用Animal声明引用指向子类对象,如何通过代码保证Animal类不能实例化对象上述问题,我们可以借助抽象方法和抽象类来实现!
抽象概述抽象,简单可理解为不具体、高度概括的,专业描述为:抽象是一种将复杂的概念和现实世界问题简化为更易于理解和处理的表示方法。在计算机科学和编程中,抽象是一种关注问题的本质和关键特征,而忽略具体实现细节的方法。在面向对象编程中,抽象是通过定义类、接口和方法来实现的,本章我们重点讨论抽象方法和抽象类,具体描述如下。
抽象方法:将共性的行为(方法)抽取到父类之后,发现该方法的实现逻辑无法在父类中给出具体的实现,就可以将该方法定义为抽象方法。
抽象类:如果一个类中存在抽象方法,那么该类就必须声明为抽象类。
抽象特点抽象方法和抽象类必须使用abstract关键字修饰实现
//抽象方法定义格式
publicabstract返回值类型方法名(参数列表);
//特别注意:抽象方法只有方法声明,没有方法的实现
//抽象类定义格式
[权限修饰符]abstractclass类名{
0或多个数据成员0或多个构造方法
0或多个成员方法}
抽象类和抽象方法的关系:使用abstract修饰的类就是抽象类抽象类可以包含,也可以不包含抽象方法包含抽象方法的类,一定要声明为抽象类
抽象类和普通类区别:抽象类必须使用abstract修饰符抽象类相对普通类,多了包含抽象方法的能力抽象类相对普通类,失去了实例化创建对象的能力
抽象类和普通类相同点:符合继承关系特点,能够使用多态机制子类可以重写从抽象类继承的方法
实例化子类对象需要借助父类构造器实现父类部分的初始化
基础案例:要求:定义一个形状类Shape,包含抽象方法getArea(),再定义其子类Circle,重写抽象方法并进行功能测试。(该案例对抽象方法、抽象类基本知识点进行考核)
.chap07.test;
//因为该类中包含了抽象方法,则该类必须声明为抽象类
//定义形状类
abstractclassShape{privateStringname;
publicShape(){System.out.println("Shape()...");
}
publicShape(Stringname){
System.out.println("Shape(String)...");
this.name=name;
}
//定义抽象方法,获取形状的面积
//因为现在形状不确定,获取面积的功能实现也是不确定的,故而定义为抽象方法
publicabstractdoublegetArea();}
//正常子类:必须重写所有抽象方法
classCircleextendsShape{
//半径
privateintr;
publicCircle(){
//super();System.out.println("Circle()...");
}
//子类构造器需要借助抽象父类构造器,完成父类部分的初始化
publicCircle(Stringname,intr){super(name);
System.out.println("Circle(String,int)...");this.r=r;
}
//重写抽象方法
@Override
publicdoublegetArea(){return3.14*r*r;
}
}
//抽象类抽象方法基础测试
publicclassTest013_AbstractBasic{
publicstaticvoidmain(String[]args){
5//1.抽象类不能实例化对象,下面一行编译报错
//Shapep=newShape();//error
//2.抽象类引用可以指向子类(非抽象类)对象Shapep=newCircle("圆",2);/
/3.实际开发中,主要多态应用
doublearea=p.getArea();
System.out.println("area:"+area);
}
}
//程序运行效果:Shape(String)...Circle(String,int)...area:12.56
动物案例结合抽象类、抽象方法基础内容补充,改良优化动物案例。
案例如下:
抽象父类Animal:
13.chap07.bean;
//该类中包含抽象方法,故而必须声明为抽象类
publicabstractclassAnimal{
//抽象类可以包含数据成员privateStringcolor;privateintage;//抽象类中可以包含抽象方法
18publicAnimal(){}
publicAnimal(Stringcolor,intage){this.color=color;
this.age=age;}@Override
publicStringtoString(){
return"Animal[color="+color+",age="+age+"]";
}
//因为eat和sleep在父类中无法确定其具体功能,故而设置为抽象方法
publicabstractvoideat();publicabstractvoidsleep();24}正常子类Cat:
.chap07.bean;
publicclassCatextendsAnimal{
//温顺度:1-10
privateintmeekValue;
publicCat(){}
publicCat(Stringcolor,intage,intmeekValue){super(color,age);
this.meekValue=meekValue;}
//子类重写所有的抽象方法
@Override
publicvoideat(){
System.out.println("猫吃鱼");
}
@Override
publicvoidsleep(){
System.out.println("躺着睡");
}
@Override
publicStringtoString(){
Stringmsg=super.toString();
msg="Cat:温顺度="+this.meekValue+","+
super.toString();returnmsg;
}
}
抽象子类Dog:
.chap07.bean;
//子类中包含抽象方法,故而必须声明为抽象类
//不可以实例化对象
//publicclassDogextendsAnimal{
publicabstractclassDogextendsAnimal{
//忠诚度
privateintloyalValue;
publicDog(){}
publicDog(Stringcolor,intage,intloyalValue){super(color,age);
this.loyalValue=loyalValue;}
//子类重写部分抽象方法
@Override
publicvoideat(){
System.out.println("狗吃肉");
}
//子类不重写sleep()抽象方法,则当前类仍旧包含抽象方法
//@Override
//publicvoidsleep(){
//System.out.println("趴着睡");
//}
@Override
publicStringtoString(){
Stringmsg=super.toString();
msg="Dog:忠诚度="+this.loyalValue+","+
super.toString();returnmsg;
}
}
基础测试:
.chap07.test;
importcom.up.chap07.bean.Animal;
importcom.up.chap07.bean.Cat;
importcom.up.chap07.bean.Dog;
publicclassTest014_Animal{
publicstaticvoidmain(String[]args){
//1.抽象父类不可以实例化对象
//Animala=newAnimal();//编译报错error
//2.抽象父类引用可以指向正常子类对象
Animalc=newCat("yellow",2,6);
c.eat();
c.sleep();
System.out.println(c);
//
//
//
//System.out.println("------------");
//3.子类如果是抽象类,不可以实例化对象
Animald=newDog("black",2,8);//编译报错
d.eat();d.sleep();
System.out.println(d);26}
}
运行效果:
额外测试1:大家也可以把Dog子类中重写的sleep方法、测试类Dog对象测试放开,运行程序,观察效果!额外测试2:在上述测试基础上,去除Dog类的abstract修饰,再次运行程序,观察效果!
2.interface
引用数据类型:类、数组、接口接口概述接口是对Java单继承的补充。Java只支持单继承(亲爹唯一),如果在开发过程中想额外增强类的功能,可以借助接口实现(可以拜师,拜多个师傅也可
以)。接口是Java中一种重要的抽象机制,它提供了一种定义行为规范和实现多态性的方式。通过合理使用接口,可以提高代码的可扩展性、可维护性和灵活性。接口是除了类和数组之外,另外一种引用数据类型,需要使用interface关键字来定义,接口最终也会被编译成.class文件,但一定要明确接口并不是
类,而是另外一种引用数据类型。
接口基础定义格式:13
[修饰符]interface接口名{
//数据成员,可以定义多个
[publicstaticfinal]数据类型数据成员=值;
//抽象方法:可以定义多个
[publicabstract]返回值类型方法名(形参列表);
}
案例如下:
//使用interface关键字来定义接口
interfaceIAction{
//数据成员,下面2行效果一样
//publicstaticfinalintNUM=10;intNUM=10;
//成员方法,下面2行效果一样
//publicabstractvoidstart();voidstart();
publicabstractvoidend();14
}
publicclassTest021_Basic{
publicstaticvoidmain(String[]args){
//接口不可以实例化对象
//IActionac=newIAction();error
System.out.println(IAction.NUM);
//接口中数据成员默认publicstaticfinal,故而下行编译报错
//IAction.NUM=20;}
}
注意1,定义类使用关键字class,定义接口使用关键字interface
注意2,接口中的数据成员,默认publicstaticfinal修饰,是常量,名称一般全大写
注意3,接口中的方法,默认publicabstract修饰,是抽象方法
补充内容(后面章节具体讨论):JDK8中,还允许在接口中编写静态方法和默认方法JDK9中,还允许在接口中编写私有方法接口实现
Java中类和类之间的关系是继承,且只能是单继承类和接口是实现关系,通过implements关键字表示,可以是单实现,也可以是多实现子类还可以在继承一个父类的同时实现多个接口
接口的实现类书写格式:13
//一个类可以同时实现多个接口
[修饰符]class类名implements接口名1,接口名2,...{
重写所有抽象方法
}
注意事项:接口属于引用数据类型的一种,它不是类,它没有构造方法接口的实现类(子类),可以是正常类(重写所有抽象方法),也可以是抽象类(包含抽象方法)
接口不能创建对象,一般用接口引用指向实现类对象
基础案例:实现类实现单个接口案例。定义IAction的实现类,重写所有抽象方法,最后进行功能测试。
.chap07.test;
interfaceIAction{
intNUM=10;
voidstart();
publicabstractvoidend();9
//定义接口的实现类
classActionImplimplementsIAction{
@Override
publicvoidstart(){
System.out.println("start开始执行");
}
@Override
publicvoidend(){
System.out.println("执行完成,end结束");}
}
publicclassTest021_Basic{
publicstaticvoidmain(String[]args){
//1.接口不能实例化对象,下面一行编译报错
//IActionic=newIAction();
//2.接口引用指向实现类对象
IActionac=newActionImpl();
//3.接口数据成员访问测试System.out.println(IAction.NUM);System.out.println(ac.NUM);System.out.println("-------------");
//4.通过接口引用调用重写方法(多态体现)
ac.start();
ac.end();
}
}
注意事项:在类和接口的实现关系中,可以使用多态,因为类和接口的实现关系,可理解为继承的一种形式。一个类可以同时实现多个接口,但需要把多个接口的抽象方法全部重写(后面案例中具体演示)。
接口继承Java中,类和类之间是单继承关系,接口和接口之间是多继承接口继承格式:
[修饰符]interface子接口extends父接口1,父接口2...{
//新增成员或抽象方法
}
例如:
interfaceRunable{voidrun();
}
interfaceFlyable{voidfly();
}
//接口多继承
interfaceActionextendsRunable,Flyable{voidsing();
}综合案例案例描述:定义一个抽象父类Animal2,再定义两个接口IJumpAble跳火圈、IJumpAble骑自行车,最后定义一个猴子类Monkey,去继承Animal2,同时实现IJumpAble、IJumpAble,进行功能测试。
复杂实现类定义格式:
[修饰符]class实现类extends父类implements接口名1,接口名2,
...{
重写所有方法;
}
案例描述:1 .chap07.test;
//定义抽象父类
abstractclassAnimal2{privateStringcolor;privateintage;publicAnimal2(){}publicAnimal2(Stringcolor,intage){this.color=color;
this.age=age;}//抽象方法
publicabstractvoideat();publicabstractvoidsleep();
}
//定义跳火圈接口
interfaceIJumpAble{
//默认修饰符为:publicabstract
voidjump();}//定义骑自行车接口
interfaceICycleAble{
publicabstractvoidcycle();}
//定义猴子类:继承Animal类,同时实现ICycleAble、IJumpAble接口classMonkeyextendsAnimal2implementsICycleAble,IJumpAble{
privateStringname;
//自定义构造方法
publicMonkey(){}
publicMonkey(Stringcolor,intage,Stringname){super(color,age);
this.name=name;}//重写从父类继承的方法
@Override
publicvoideat(){
System.out.println("猴子"+name+"喜欢吃大桃子");
}@Override
publicvoidsleep(){
System.out.println("猴子"+name+"喜欢在树上睡觉");
}
//重写从接口获取的抽象方法
@Override
publicvoidcycle(){
System.out.println("猴子"+name+"能够骑自行车");}
@Override
publicvoidjump(){
System.out.println(name+"能够跳火圈");}63
}
//测试类
publicclassTest024_Monkey{
//基本测试
publicstaticvoidmain(String[]args){
//1.抽象类不能实例化对象,但可以定义引用指向子类对象Animal2a=newMonkey("yellow",2,"小悟空");
//2.借助抽象类引用,只能访问抽象类中具有的方法
a.eat();a.sleep();//下面两行编译报错【多态:编译看左边,运行看右边】
//a.jump();
//a.cycle();
System.out.println("****************");
//3.用接口引用指向实现类对象
ICycleAblec=newMonkey("yellow",5,"马戏团小猴");
//4.借助接口引用调用接口中重写方法
c.cycle();
//注意:接口引用类型只能调用接口中具备的方法【多态:编译看左边,运行看右边】
//下面三行编译报错
//c.eat();error
//c.sleep();error
//c.jump();error
System.out.println("****************");
//5.如果想要调用父类方法或其他接口方法,可以借助类型转换实现
//注意,一定要借助instanceof额外判断引用指向对象的类型
if(cinstanceofMonkey){
Monkeym=(Monkey)c;
m.eat();
m.sleep();}System.out.println("****************");
if(cinstanceofIJumpAble){
IJumpAblej=(IJumpAble)c;
j.jump();
}
}
注意事项:接口多态应用时,编译看左边,运行看右边即接口引用只能调用接口中包含的方法,成功调用的是重写以后的方法
类接口关系类与类的关系继承关系,只能单继承,但是可以多层继承类与接口的关系
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
接口与接口的关系继承关系,可以单继承,也可以多继承
接口新特性1)JDK8新特性:接口可以包含静态方法和默认方法案例如下:
.chap07.test;
//JDK8中接口可以添加默认方法和static方法
interfaceJDK8Action{
//接口中静态常量
StringOPS_MODE="auto";
//接口中抽象方法voidstart();voidstop();//下面是JDK8新特性
//默认方法
publicdefaultvoiddFun(){System.out.println("indefaultfun()...");
}
//静态方法
publicstaticvoidsFun(){System.out.println("instaticfun()...");
}
}
classDemo01implementsJDK8Action{@Override
publicvoidstart(){
System.out.println("重写start()...");
}
@Override
publicvoidstop(){
System.out.println("重写stop()...");
}
}
publicclassTest026_JDK8{
publicstaticvoidmain(String[]args){
//1.接口引用指向实现类对象
JDK8Actiona=newDemo01();
//2.调用实现类重写方法
a.start();a.stop();//3.调用default方法
a.dFun();
//4.JDK8中接口可以定义static方法,但不能通过接口引用调用,只能通过接口名调用
//a.sFun();编译报错
JDK8Action.sFun();
}
}
注意事项:JDK8接口可以定义static方法,但不能通过接口引用调用,只能通过接口名调用
2)JDK9新特性:接口可以包含私有方法案例如下:
.chap07.test;
//使用interface关键字来定义接口
interfaceJDK9Action{
//接口中的静态常量
StringOPS_MODE="auto";
//接口中的抽象方法voidstart();
//私有方法jdk9以下报错
privatevoidrun(){System.out.println("privaterun()...");
}
}
classDemo02implementsJDK8Action{@Override
publicvoidstart(){
System.out.println("重写start()...");
}
}
//测试类
publicclassTest026_JDK9{
publicstaticvoidmain(String[]args){
//1.接口引用指向实现类对象JDK9Actiona=newDemo02();
//2.调用实现类重写的抽象方法
a.start();
//3.调用接口private方法
a.run();}
}
注意:测试上述案例需要用合适的JDK版本
常见面试题接口和抽象类有什么区别?如何选择?
语法结果区别:
定义方式:抽象类通过使用abstract关键字来定义,而接口使用interface关键字来定义
实现方式:一个类可以继承(extends)一个抽象类,而一个类可以实现(implements)多个接口
构造函数:抽象类可以有构造函数,而接口不能有构造函数方法实现:抽象类可以包含具体的方法实现,而接口只能包含抽象方法,即没有方法体的方法声明
多继承:Java不支持多继承,一个类只能继承一个抽象类,但可以实现多个接口
数据成员:抽象类可以包含普通数据成员和static数据成员,而接口只能包含staticfinal修饰的数据成员
设计理念区别:不同的实现类之间体现likea的关系,接口更加强调行为规范的定义,适用于多个类具有相同行为规范的情况。
例如:飞机具备飞翔的行为,鸟也具备飞翔的行为,此时我们就可以定义接口包含抽象方法fly(),然后让飞机和鸟分别去实现该接口。飞机likea鸟,因为它们都会fly()。
子类和抽象父类体现的是isa的关系,抽象类归根到底还是类,它比较特殊,不能被实例化,只能被继承。抽象类用于定义一种通用的模板或者规范,其中可包含了一些具体数据成员、方法实现和抽象方法声明。
例如:前面案例中的形状类Shape,它里面包含方法getArea(),但该方法功能不确定,所以定义成抽象方法,而包含了抽象方法的类Shape也必须被声明为抽象类。定义子类圆形类,其getArea()方法功能是明确的,则子类中重写方法。
结论:如果仅仅是要额外扩展已存在类的功能,则选择定义接口,让类去实现接口如果需要创建一组相关的类,且这些类之间有一些共同的行为和属性,那么可以定义一个类作为这些类的父类。如果不想实例化父类对象,则可以把这个父类设置为抽象类。
3.内部类
在一个类的内部再定义另外的一个类,这就是内部类。正常类形式,也称为外部类或顶级类:
publicclassTest{
}
classA{
}内部类形式:
//外部类
publicclassOuter{
//内部类
publicclassInner{
}
}
内部类一共分为四种形式:成员内部类静态内部类局部内部类匿名内部类
成员内部类在类中,除了可以定义成员方法、成员变量,还可以定义成员内部类。
成员内部类定义格式:
//外部类
[修饰符]class外部类{
//省略...
//内部类
[权限修饰符]class内部类{0或多个数据成员
0或多个构造方法0或多个成员方法
//注意,不可以包含static成员或方法}
}
内部类对象实例化格式:外部类名.内部类名对象名=外部类对象.内部类对象;例:Outer.Inneroi=newOuter().newInner();
内部类中访问外部类中同名成员:外部类名.this.成员名例:intv=Outer.this.o_num;
注意事项:成员内部类中可以直接访问外部类中所有成员和方法(含private)在外部类中可以直接访问内部类所有的成员和方法(含private),但必须借助内部类对象
成员内部类内部不能定义static
成员或方法案例展示:定义外部类Outer,包含数据成员num,其内部定义内部类Inner,也包含数据成员num,测试内部类基本用法。
.chap07.test;
//1.定义外部类
classOuter{
privateinto_num=10;privateintnum=20;
//2.定义成员内部类
classInner{
privateinti_num=30;
//关键点:包含数据成员与外部类中成员同名
privateintnum=40;
//3.成员内部类不能定义static成员或方法
//publicstaticints_num=50;error
//publicstaticvoids_test(){}error
//4.定义内部类方法,测试对外部类成员的访问
voidshow(){
//内部类中可以直接访问外部类所有成员System.out.println("inInner,o_num:"+o_num);
//局部变量
intnum=50;
System.out.println("inInner,num:"+num);
//50
//访问内部类中数据成员num
System.out.println("inInner,num:"+this.num);
//40
60//访问外部类中同名成员num,固定格式:外部类名.this.成员名
System.out.println("inInner,num:"+Outer.this.num);//20
}
}
//5.定义外部类方法,测试访问内部类成员
//注意:外部类中可以访问内部类所有成员(含private),但必须借助内部类对象
publicvoiddisp(){
//实例化内部类对象格式:
//外部类.内部类引用=外部类对象.new内部类(实参);Outer.Innerinn=this.newInner();System.out.println("inOuter,i_num:"+inn.i_num);//简化格式:
//内部类引用名=new内部类(实参列表);
//解析:
//在外部类方法中,Inner类名前默认加'Outer.',newInner()前默认加'this.'
Innerinn2=newInner();System.out.println("inn2.i_num:"+inn2.i_num);
}
}
//测试类
publicclassTest031_Member{
publicstaticvoidmain(String[]args){
//实例化成员内部类对象
//外部类.内部类引用=外部类对象.new内部类(实参);
Outerout=newOuter();Outer.Innerinn=out.newInner();inn.show();System.out.println("-------------");
62Outer.Innerinn2=newOuter().newInner();inn2.show();
}
注意,上述代码编译成功后,外部类和内部类都会生成对应的class文件
JavaAPI中的使用的成员内部类:java.util.ArrayList类中,就定义了好几个成员内部类,并且还是private
修饰的思考,什么情况下会使用内部类?在对事物进行抽象的时候,若一个事物内部还包含其他事物,就可以考虑使用内部类这种结构。
例如,汽车(Car)中包含发动机(Engine),这时,Engine类就可以考虑(非必须)使用内部类来描述,定义在Car类中的成员位置。这样设计,既可以表示Car和Engine的紧密联系的程度,也可以在Engine类中很方便的使用到Car里面的属性和方法。
注意,这是从程序中,类和类之间的关系和意义进行考虑而设计的,其实这里即使不使用内部类的结构,使用普通的俩个类也能完成功能,但是内部类的结构设计会加符合实际意义,也能够好的完成功能,因为内部类访问外部的属性和方法会更加容易。
静态内部类静态内部类属于成员内部类中的一种,其需要static关键字进行修饰。
静态内部类定义格式:
//外部类
[修饰符]class外部类{
//省略...
//静态内部类
[权限修饰符]staticclass内部类{0或多个数据成员
0或多个构造方法0或多个成员方法
//注意:静态内部类成员方法内,只能访问外部类static成员或方法}
}
对象定义格式:外部类名.内部类名对象名=new外部类名.内部类名();
静态方法访问格式:外部类名.内部类名.方法名();如果在外部类中,外部类名可以省略。
注意事项:相对于成员内部类,静态内部类中可以定义static成员和方法静态内部类成员方法内,只能访问外部类static成员或方法【静态只能访问静态】
外部类方法可以访问静态内部类所有成员及方法
案例如下:.chap07.test;
classOuter2{
privateinto_num=10;privatestaticints_num=20;//定义static内部类
staticclassInner{privateinti_num=30;
//静态内部类中可以定义static成员
privatestaticintis_num=40;
//static成员方法只能访问外部类static成员【静态只能访问静态】
publicstaticvoidsFun(){
//编译报错
//System.out.println("Outer2o_num:"+o_num);
System.out.println("Outer2s_num:"+Outer2.s_num);
System.out.println("Outer2s_num:"+s_num);}//普通成员方法
voidshow(){
//1.访问内部类中数据成员
System.out.println("inInner,num:"+this.i_num);//30
System.out.println("inInner,is_num:"+Inner.is_num);//40//2.静态内部类方法中只能访问外部类的static成员
//编译报错
//System.out.println("inInner,o_num:"+o_num);
//3.访问外部类static成员,下面两行效果相同System.out.println("inInner,s_num:"+s_num);//2035
System.out.println("inInner,s_num:"+Outer2.s_num);//20
}
}
//外部类方法可以访问静态内部类所有成员及方法publicvoiddisp(){
//Outer2.Innerinn=newInner();Outer2.Innerinn=newOuter2.Inner();
System.out.println("inOuter,i_num:"+inn.i_num);System.out.println("inOuter,is_num:"+
Inner.is_num);
System.out.println("inOuter,is_num:"+inn.is_num);}
}
publicclassTest032_Static{
publicstaticvoidmain(String[]args){
//1.静态内部类调用static方法
//编译报错,不识别Inner,会把Inner当成外部类去查找
//Inner.sFun();
//正确写发
Outer2.Inner.sFun();
System.out.println("---------------");
//2.静态内部类普通方法调用
//内部类对象固定定义格式
Outer2.Innerinn=newOuter2.Inner();inn.show();
System.out.println("----------------");
//3.外部类对象访问内部类成员Outer2outer=newOuter2();outer.disp();70}
注意,在静态内部类中访问不了外部类中的非静态属性和方法
JavaAPI中静态内部类案例:java.lang.Integer类中,就定义了一个静态内部类,并且还是private修饰的
局部内部类在外部类的方法中定义的内部类,我们称为局部内部类,它的作用范围只是在当前方法中。局部内部类是最不常用的一种内部类,大家了解即可。
定义格式:
[修饰符]class外部类{
//省略...
//成员方法
[修饰符]返回值类型方法名(形式参数列表){
//功能实现省略...
//局部内部类定义class成员内部类名{
0或多个数据成员0或多个构造方法0或多个成员方法
}
//注意:局部内部类的作用范围在当前方法中,只能在该方法中使用
}
}
注意事项:局部内部类只能在定义的方法中使用。
案例展示:.chap07.test;
//外部类
classOuter3{
privateintnum=10;
//包含局部内部类的方法
publicvoidinnerFun(){
//下面两行效果一样
//intnum=20;finalintnum=20;//在方法内部,定义局部内部类
classInner{
privateinti_num=30;
publicvoidtest(){
System.out.println("局部变量num:"+num);System.out.println("内部类成员变量this.i_num:"
+this.i_num);
System.out.println("外部类成员变量
Outer3.this.num:"+Outer3.this.num);
//方法中声明的局部变量,只要在内部类中使用,默认会加上final修饰
//所以下面一行编译会报错:给final变量赋值
//num=60;}
}
//创建局部内部类对象
Innerinn=newInner();
//访问内部类私有成员
System.out.println(inn.i_num);
System.out.println("-------------");
//调用内部内方法
inn.test();37}}
//测试类
publicclassTest033_Local{
publicstaticvoidmain(String[]args){
Outer3outer=newOuter3();
outer.innerFun();
}
}
运行效果:
面试常考:JDK8中,方法中定义的局部变量,如果在局部内部类中对其访问操作,那么这个局部变量会默认加上final修饰,其值不能改变。
匿名内部类匿名内部类,是一种没有名字的内部类,本质上是一个特殊的局部内部类(定义在方法内部)。在之后的代码中,匿名内部类是使用最多的内部类(必须掌握)。
常规接口、抽象类的操作步骤:声明一个类,去实现这个接口,或去继承抽象父类重写所有抽象方法用接口或引用去指向子类或实现类对象通过接口或抽象类引用去调用重写的方法在这个过程中,我们的核心任务是重写抽象方法,最后再调用这些重写的方法。所以上述过程过于复杂,使用匿名内部类,可以大大简化上述步骤!