IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Java 类、接口、内部类详解 -> 正文阅读

[Java知识库]Java 类、接口、内部类详解

第十章

抽象类 、 接口 与内部类

10.1抽象类

10.1.1 抽象类引出

当一个父类的某一些方法需要声明,但是呢又不知道该如何去实现,一般都是子类去具体实现的,这时候就可以将其父类设置为抽象类。

例:有一个动物,有eat方法,但是具体怎么实现不知道

public class Abstract01 {
}
abstract class Animal{
    private String name;
    //以下代码说实话没有具体意义,只要求声明一下,所以直接将其设计为abstract抽象类
    //abstract抽象类就是没有具体实现方法的方法
    //没有实现方法也就是没有方法体
    //需要注意:在一个类中有abstract抽象方法的时候,则该类要设计为abstract类
    /*public void eat(){
        System.out.println("这是动物吃东西的方法...");
    }*/
    public abstract void eat();
}
1、abstract抽象类就是没有具体实现方法的方法
2、需要注意:在一个类中有abstract抽象方法的时候,则该类要设计为abstract类
10.1.2 抽象类介绍

1、用abstract关键字来修饰一个类时,这个类就叫抽象类
语法:
访问修饰符 abstract 类名{
}

2、用abstract关键字来修饰一个方法时,这个方法就是抽象方法
语法:
访问修饰符 abstract 返回类型 方法名 (参数列表); //注意:是没有方法体的

3、抽象类更多在于设计,是设计者设计好后,让子类继承并实现抽象类()

4、抽象类,是考官比较爱问的知识点,在框架和设计模式中使用较多

10.1.3 抽象类细节

1、抽象类不能被实例化
在这里插入图片描述

2、abstract抽象类不一定要包含abstract抽象方法,也可以有实现的方法
在这里插入图片描述

3、类里面有abstract抽象方法那么该类一定是abstract抽象类
在这里插入图片描述

4、abstract抽象只能去修饰类和方法,不能修饰属性和其他
在这里插入图片描述
5、抽象类可以有任意成员(因为抽象类的本质始终还是一个类),比如有:非抽象的方法、构造器、静态属性等等

6、抽象方法不能有主体
在这里插入图片描述
7、如果一个类继承了抽象类,那么它必须实现抽象类的所有抽象方法,除非它自己也声明为抽象类
在这里插入图片描述

8、抽象方法不能使用private、final 和 static 来修饰,因为这些关键字和重写是像制约的

在这里插入图片描述

10.1.4抽象类最佳实践----模板设计模式

例:
分别计算多个类中,完成不同任务所需要的时间,
AA类 计算1+…+1000000所花的时间
BB类 计算1*…500000的时间

思路:在两个类中分别通过 结束时间-开始时间 去计算不同类完成某工作的时间

public class AA {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();//获取当前时间
        long sum = 0;
        for (long i = 0; i < 1000000; i++) {
            sum += i;
        }
        long end = System.currentTimeMillis();
        System.out.println("AA 所花的时间 "+(end - start));
    }
}

public class BB {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();//获取当前时间
        long sum = 0;
        for (long i = 0; i < 500000; i++) {
            sum *= i;
        }
        long end = System.currentTimeMillis();
        System.out.println("BB 所花的时间 "+(end - start));
    }
}

以上解法,可以看出方法比较繁琐,两个类中有共同的语句了,所以,我们可以通过将主要的计算方法单独包装成一个方法,然后再用的时候去调用即可,优化后代码:

public class AA {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();//获取当前时间
        work();
        long end = System.currentTimeMillis();
        System.out.println("AA 所花的时间 "+(end - start));
    }
    //将主要计算功能单独成一个方法,然后在main方法中去调用即可
    public static void work(){
        long sum = 0;
        for (long i = 0; i < 1000000; i++) {
            sum += i;
        }
    }
}
public class BB {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();//获取当前时间
        work();
        long end = System.currentTimeMillis();
        System.out.println("BB 所花的时间 "+(end - start));
    }
    //将主要计算功能单独成一个方法,然后在main方法中去调用即可
    public static void work(){
        long sum = 0;
        for (long i = 0; i < 500000; i++) {
            sum *= i;
        }
    }
}

以上方法明显比第一个简单一点,但是还是比较繁琐,要是当要计算的类比较多的时候,这样写也是很繁琐的,所以,通过以上代码我们可以看出来,不同类的基本结构都是一样的,都是先获取开始时间,然后运行功能方法,运行完后获取结束的时间,然后用结束时间去 - 开始时间 ,这样就可以获得类完成功能的时间,所以,我们可以将具体的功能方法封装成一个abstract抽象方法,子类具体去实现,然后将时间具体时间也给封装成一个方法
优化后的代码

public class AA extends Template {
    //将主要计算功能单独成一个方法,然后在main方法中去调用即可
    public  void work(){
        long sum = 0;
        for (long i = 0; i < 1000000; i++) {
            sum += i;
        }
    }
}
public class BB extends Template{
    //将主要计算功能单独成一个方法,然后在main方法中去调用即可
    public  void work(){
        long sum = 0;
        for (long i = 0; i < 500000; i++) {
            sum *= i;
        }
    }
}
public abstract class Template {
    public abstract void work();//定义一个抽象的方法,利用子类去重写
    public void  calculateTime(){//编写一个计算时间的方法去计算时间
        long start = System.currentTimeMillis();//获取当前时间
        work();
        long end = System.currentTimeMillis();//获取结束时间
        System.out.println("执行时间:"+(end - start));
    }
}
public class Abstract04 {
    public static void main(String[] args) {
        new AA().calculateTime();
        new BB().calculateTime();
    }
}

10.2 接口

10.2.1 接口的基本介绍

接口(interface)就是给一些没有实现的方法,封装在一起,到某一个类要使用的时候,再根据实际情况去把这些方法写出来。

语法:
interface 接口名{
//属性
//方法(1、抽象方法 2、默认实现方法(需要用default修饰) 3、静态方法)
}

当一个类要去使用接口的时候
语法:
class 类名 implement 接口名{
//自己的属性
//自己的方法
//注意:必须要实现接口的抽象方法
}

注意:
1、在 jdk7.0 前,接口里所有的方法都是抽象方法,也就是没有方法体的
2、在 jdk8.0 后,接口可以有静态方法、默认方法,也就是接口中可以有方法的具体实现

public interface Interface01 {//创建一个接口
    //1、接口可以有自己的属性,但是必须要初始化赋值,不然会报错
    public int sum = 10;
    public String name="acd";
    
    //2、可以有抽象方法,abstract关键字 可以不写,默认就是抽象方法
    public void cry();
    //public abstract void cry1();
    
    //3、可以是默认实现方法,但是必须要default去修饰
    default public void cry2(){
        System.out.println("默认实现方法...");
    }
    
    //4、可以是静态方法
    public static void cry3(){
        System.out.println("静态方法...");
    }
}

public class Interface01Text implements Interface01{
    //用类去实现接口,因为接口中有抽象方法,所以必须要去实现所有抽象方法,不然会报错
    @Override
    public void cry() {
        System.out.println("类实现接口cry1");
    }
}
10.2.2接口的细节

1、接口不能被实例化
在这里插入图片描述

2、接口中,所有的方法都是 public,而且 public 已经是默认的了,所以也可以不写,接口中的抽象方法,也可以不用 abstract 去修饰
在这里插入图片描述
3、一个普通的类实现接口,就必须将该接口的所有方法都实现,主要是抽象方法必须实现,要是接口中有default 修饰的默认实现方法,或者static修饰的静态方法,则可以不用去实现(11.1中的代码可以深刻的体现)
在这里插入图片描述
如果需要实现的方法较多的时候,可以用快捷键(alt + enter)去一键补全
在这里插入图片描述
4、抽象类实现接口,可以不用实现接口的方法
在这里插入图片描述
5、一个类,可以实现多个接口,每一个接口的方法都必须要去实现,和上面细节3的要求一样
在这里插入图片描述
6、接口中的属性,只能是final的,修饰符是 public static final 去修饰的,并且必须要初始化
在这里插入图片描述
7、接口中属性的访问形式是: 接口名 . 属性名
在这里插入图片描述
8、接口不能继承其他的类,但是可以继承多个其他接口(接口与接口之间是 extends 继承,接口和类之间是 implement 实现)

注意:一个类要是继承了有父类的接口,那么这个类就必须要去实现这个接口本身以及所有父类的所有为实现的抽象方法
在这里插入图片描述
9、接口的修饰符,只能是 public 和 默认,这一点和类的修饰符是一样的
在这里插入图片描述

10.2.3接口练习

判断下面代码是否有语法错误,没有的话,输出什么?

public class InterfaceText01 {
public static void main(String[] args) {
    BB bb = new BB();
    System.out.println(bb.a);
    System.out.println(Ainter.a);
    System.out.println(BB.a);
    }
}
interface Ainter{
int a = 20;
}
class BB implements Ainter{ }

解答:

package com.xioawang.interface_;

public class InterfaceText01 {
    public static void main(String[] args) {
        BB bb = new BB();
        System.out.println(bb.a);//因为BB实现了Ainter这个接口,所以类的实例对象是可以去访问接口属性的,所以输出20
        System.out.println(Ainter.a);//接口名去调用接口属性没问题,输出20
        System.out.println(BB.a);//因为BB实现了Ainter这个接口,所以可以用类名.属性名去访问属性,所以输出20
    }
}
interface Ainter{
    int a = 20;
}
class BB implements Ainter{ }

输出:
20
20
20
10.2.4实现接口和继承的区别

1、接口的实现,是对Java单继承机制的补充

例:
现在有一只小猴子、一只老猴子、一只鸟、一条鱼;小猴子是老猴子的子类,从生下来就继承了老猴子会爬树的行为,但是呢,小猴子要是想要会飞,那么就只能去找鸟去学习飞翔,相当于就是小猴子去实现鸟飞翔的行为,小猴子要是想学会游泳,就去找鱼学习,然后去实现鱼游泳的行为

package com.xioawang.interface_;

public class InterfaceVsExtends {
    public static void main(String[] args) {
        LittleMonkey littleMonkey = new LittleMonkey("江仔");
        littleMonkey.climing();
        littleMonkey.flying();
        littleMonkey.swimming();
    }
}
class Monkey{
    private String name;
    public Monkey(String name) {
        this.name = name;
    }
    public void climing(){
        System.out.println(name+" 会爬树...");
    }
    public String getName() {
        return name;
    }
}
interface Bird{
    void flying();
}
interface Fish{
    void swimming();
}
class LittleMonkey extends Monkey implements Bird,Fish{//继承一个类后,就有了父类的功能
    public LittleMonkey(String name) {
        super(name);
    }
    @Override
    public void flying() {
        System.out.println(getName()+" 会飞翔...");
    }
    @Override
    public void swimming() {
        System.out.println(getName()+" 会游泳...");
    }
}

结论:
1、当子类继承父类了后,子类就自动拥有了父类的功能
2、如果子类还想要拥有其他的功能,那么可以用实现接口的方式去扩展
3、实现接口,是对 Java 单继承机制的一种补充

2、接口和继承解决的不同问题
继承的价值:解决代码的复用性和可维护性
接口的价值:设计好各种规范(方法),让其它类去实现这些方法,也就是接口更加灵活

3、接口比继承更加灵活
接口比继承更加灵活,继承是 is - a 的关系,而接口是 like - a 的关系,通俗来说就是,猫是一个动物,但是同时可以通过接口去实现,像人一样的动作行为

4、接口在一定程度上实现代码解耦
在接口规范性 + 动态绑定机制 的基础上去实现代码的解耦

10.2.5 接口的多态性

1、接口的多态参数

也就是,接口的引用可以指向实现了接口类的对象实例;

语法:

接口类名 接口变量名 = new 实现接口的类名();

在这里插入图片描述

public class InterfacePloyParameter {
    public static void main(String[] args) {
        //接口体现多态
        //接口变量cInter 可以指向 实现了 DInterface 的对象实例
        DInterface cInter = new C();
        cInter = new D();
        //继承体现多态
        //父类BBB的变量 bbb 可以指向 继承了BBB父类的所有子类的对象实例
        BBB bbb = new CC();
        bbb = new DD();
    }
}
interface DInterface{}
class C implements DInterface{}
class D implements DInterface{}

class BBB{}
class CC extends BBB{}
class DD extends BBB{}

2、接口的多态数组

例:
在 Usb数组中,存放 Phone 和 Camera 对象,Phone类还有一个特有的方法call(),请遍历 Usb 数组,如果存放Phone对象,除了调用Usb 接口定义的方法外,还需要调用Phone的特有方法call()

public class InterfacePloyArr {
    public static void main(String[] args) {
        Usb [] usb = new Usb[3];//先定义一个usb数组去存放对象
        usb[0] = new Phone("华为");
        usb[1] = new Phone("苹果");
        usb[2] = new Camera("索尼");
        //可以存放多种类型的类对象,就已经说明了接口有多态数组了
        for (int i = 0; i < usb.length; i++) {
            System.out.print(usb[i].message());
            if (usb[i] instanceof Phone) {//判断运行类型
                System.out.println(" "+((Phone) usb[i]).call());
            }
        }
    }
}
interface Usb{
    String message();
}
class Phone implements Usb{
    private String name;
    public Phone(String name) {
        this.name = name;
    }
    public String call(){
        return getName()+"正在打电话ing...";
    }
    public String getName() {
        return name;
    }
    @Override
    public String  message() {
        return "我是:"+getName()+"手机";
    }
}

class Camera implements Usb{
    private String name;
    public Camera(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }

    @Override
    public String message() {
        return "我是: "+getName()+"相机";
    }
}

3、接口的多态传递
也就是两个接口之间存在继承关系,实现子接口的类相当于也实现了父接口
代码解释如下:

package com.xioawang.interface_;
/*演示接口的多态传递*/
public class InterfacePloyPass {
    public static void main(String[] args) {
        Finterface finterface = new E();//Finterface接口的变量,指向E类的实例对象,因为E类实现了Finterface接口
        
        Einterface einterface = new E();//因为Finterface接口继承了Einterface接口,
        // Einterface接口的变量可以指向E类的实例对象,就证明了,接口之间有传递性
        
        Dinterface dinterface = new E();//FALSE,因为Finterface接口、Einterface接口与Dinterface接口之间没有继承关系,
        // 所以E类的实例对象不能够实现Dinterface
    }
}
interface Dinterface{}
interface Einterface{}
interface Finterface extends Einterface{}//接口与接口之间是继承关系,接口和类之间是实现关系
class E implements Finterface{ }

上面代码中要特别注意这句:
在这里插入图片描述

10.3 内部类

10.3.1 四种内部类

内部类基本介绍:

一个类里面又完整的嵌套了另一个类结构,被嵌套的类称为内部类,嵌套内部类的类叫做外部类,内部类是类的第五大成员
【类的五大成员:属性、方法、构造器、代码块、内部类】

内部类特点:

内部类可以直接访问类的私有属性,同时可以体现类与类之间的包含关系

内部类基本语法:

class Outer {   //外部类
   class inner{    //内部类
    }
}
class Other{//其他类
}

快速入门案例:

/*类介绍*/
public class InnerClass01 {//外部其他类
    public static void main(String[] args) {
    }
}
class Outer{//外部类
    private String name;//外部类的属性
    public void cry(){  }//外部类的方法
    public Outer(String name) {//外部类的构造器
        this.name = name;
    }
    class Inner{//内部类
    }
}

注:内部类一共有四种:

1、定义在外部类的局部位置上(比如在方法内)
①局部内部类
②匿名内部类(重点!!!!)

2、定义在外部类的成员位置上:
①成员内部类(没有static修饰)
②静态内部类(使用static修饰)
10.3.2 局部内部类

基本介绍:

局部内部类就是定义在外部类的局部位置上,比如在方法中(通常也是在方法中),并且有类名 

说明:

1、可以直接访问外部类的所有成员方法,包括私有的成员方法

在这里插入图片描述

2、不能添加访问修饰符,因为它的地位就是一个局部变量,局部变量是不能使用修饰符的。
     但是可以用 final 修饰,因为局部变量也可以使用final

在这里插入图片描述

**3、作用域:仅仅只能在定义它的方法或者代码块中**

4、局部内部类可以直接访问外部类的成员

5、外部类 访问 局部内部类 的成员,访问方式:先创建对象,再访问(注意:必须要在作用域内)

在这里插入图片描述
注:只能在局部内部类所在的外部类的方法中去创建对象,并且是在类创建完了后,才能实例化对象

6、外部其他类 ---不能访问----》局部内部类  (因为 局部内部类的地位是一个局部变量,作用域也只能在其所在的方法或者代码块中)

7、如果外部类的成员 和 局部内部类的成员 重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用: 外部类名.this.成员
/*局部内部类演示*/
public class JuBuInnerClass {
    public static void main(String[] args) {
        Outer05 outer05 = new Outer05();
        outer05.hi();
        System.out.println("main方法中的 Outer05.this 的hashcode值= "+outer05);
    }
}
//6、如果外部类的成员 和 局部内部类的成员 重名时,默认遵循就近原则
class Outer05{
    private int n1 = 1;
    public void hi(){
        class Inner05{
            private int n1 = 2;
        }
        Inner05 inner05 = new Inner05();
        //如果想访问外部类的成员,则可以使用: 外部类名.this.成员
        System.out.println("n1 = "+n1+" 外部类的n1 = "+ Outer05.this.n1);
        //7、Outer05.this 的本质就是外部类的对象,
        // 即 是哪个实例化对象调用了n1,就是那个对象,这里是outer05对象
        System.out.println("内部的 Outer05.this 的hashcode值= "+Outer05.this);
        //如果这里输出的hashcode值和main方法中的一样,则证明 7 结论没错
    }
}
输出:
n1 = 1 外部类的n1 = 1
内部的 Outer05.this 的hashcode值= com.xioawang.InnerClass.Outer05@1b6d3586
main方法中的 Outer05.this 的hashcode值= com.xioawang.InnerClass.Outer05@1b6d3586

注:<外部类名.this.成员> 中 <外部类名.this> 的本质是一个对象,即 是哪个实例化对象调用了含有局部内部类的方法,就是那个

10.3.3匿名内部类
10.3.3.1匿名内部类介绍

解析:
1、本质是一个类
2、内部类
3、该类没有名字(不能自己写,jdk底层会给分配一个:外部类名 $1)
4、匿名内部类同时也是一个对象(因为jdk底层再创建一个匿名内部类的时候,就会立即去实例化一个对象)

说明:
1、匿名内部类是定义在外部类的局部位置的,比如在方法中,或者代码块中

基本语法:
new 类 或 接口(参数列表){
类体
}

10.3.3.2匿名内部类使用案例

案例:基于接口的匿名内部类

package com.xioawang.InnerClass;
/*演示基于接口的匿名内部类使用*/
public class AnonymousInnerClass {
    public static void main(String[] args) {
        A a = new A();
        a.classCry();
    }
}
class A{//外部类
    public void classCry(){//基于接口的匿名内部类
        //1.需求:用接口Ia,并创建一个对象
        //2.传统方法,写一个类,去实现接口Ia,再创建一个对象
        //3.所要创建的对象,只使用一次,后面不再用,要是按照传统方法,就很浪费资源
        //4.这时可以通过匿名内部类来简化开发代码
        //5.tiger的编译类型:Ia接口
        //6.tiger的运行类型:匿名内部类  名称:外部类的名称+$1,这里就是A$1
        /*
        底层代码:会分配一个 类名 :外部类的名称+$1
        * class XXXX implement Ia{
        *   public void InterCry() {
                System.out.println("老虎叫...");
            }
        * }
        * */
        //7.jdk底层再创建匿名内部类,就立即创建了一个匿名内部类的实例,
        //   并且把地址返回给了编译类型的名称,这里就是tiger
        //8.匿名内部类使用一次就不能再使用了
        Ia tiger = new Ia(){//接口是不能直接new的,但是后面有了方法体以及用tiger去接收
            @Override
            public void InterCry() {
                System.out.println("老虎叫...");
            }
        };
        tiger.InterCry();
        System.out.println("匿名内部类的名称:"+tiger.getClass());//获取某一个类id类名称
    }
}
interface Ia{//接口
    public void InterCry();
}
输出:
老虎叫...
匿名内部类的名称:class com.xioawang.InnerClass.A$1

总结:
相当于就是,一个对象只希望被用一次,但是呢,按照传统方法再去创建一个类去实现接口再实例化对象,就会觉得很代码繁琐,为了简化,就可以创建一个匿名内部类去实现接口,再代码的底层中,会有一个底层分配的类去实现这个接口,并且实例化一个对象,匿名内部类的方法体就是你所要实现的方法。同时,基于接口的时候,接口中任何一个方法,都必须要在匿名内部类中去实现。毕竟这个类要去实现这个接口。

案例:基于类的匿名内部类

/*演示基于类的匿名内部类使用*/
public class AnonymousInnerClass {
    public static void main(String[] args) {
        A a = new A();
        a.classCry();
    }
}
class A{//外部类
    public void classCry(){
    //1.创建一个对象,要是后面没用{ 方法体 },那么这就是一个普通的类,要是又{ }那就是一个匿名内部类
        //2.b的编译类型:B类
        //3.b的运行类型:A$1 这个匿名内部类,如果后面还有匿名内部类,$后面的数字一次类推就行
        //4.底层代码中:匿名内部类
        //5.匿名内部类所传入的参数是要传给构造器的
        //6.同时也直接返回了匿名内部类的对象
        /*
        * class A$1 extends B {
        *     方法体内就是你所要执行的语句这里相当于是
        *  @Override
            public void test() {
                System.out.println("匿名内部类 重写tset方法....");
            }
        * }
        */
        B b = new B("Tom"){
            //匿名内部类可以重写方法
            @Override
            public void test() {
                System.out.println("匿名内部类 重写tset方法....");
            }
        };
        b.test();
        System.out.println("b对象的运行类型:"+b.getClass());//验证3的正确性
    }
}
class B{
    private String name;
    public B(String name) {
        System.out.println("所接收到的name:"+name);
    }
    public void test(){
    }
}
输出:
所接收到的name:Tom
匿名内部类 重写tset方法....
b对象的运行类型:class com.xioawang.InnerClass.A$1

总结:
基于类的匿名内部类,需要i注意:有{ }的才是匿名内部类,没有{ }的不是匿名内部类,同时{ }的方法体是可以不需要去写或者是重写的,并且,再底层代码中,匿名内部类的实现是通过 extends 继承的,同时创建过后也会直接实例化传给所接收的对象,并且参数列表的值,会传递到构造器中去。

案例:基于抽象类的匿名内部类

/*演示基于抽象类的匿名内部类使用*/
public class AnonymousInnerClass {
    public static void main(String[] args) {
        A a = new A();
        a.classCry();
    }
}
class A{//外部类
    public void classCry(){
     C c = new C(){
            @Override
            void eat() {
                System.out.println("抽象类的匿名内部类中的抽象方法必须要写");
            }
        };
        c.eat();
    }
}
abstract class C {//抽象类
    abstract void eat();
}
输出:
抽象类的匿名内部类中的抽象方法必须要写

总结:
和基本的 基于类的匿名内部类是一样的,最大的区别就是,基于抽象类的时候,抽象类的所有抽象方法都要去实现

10.3.3.3匿名内部类细节

1、从匿名内部类的语法中可以看出,它既是一个类的定义,同时也是一个对象,所以调用匿名内部类的方法也是很多种:

① 创建一个类对象,再用对象名去调用方法,这时的匿名内部类就是类的定义
在这里插入图片描述

②直接通过匿名内部类去调用方法,这时,匿名内部类就是一个对象
在这里插入图片描述
2、匿名内部类可以访问外部类的所有成员属性,包括私有
在这里插入图片描述

3、不能添加访问修饰符,因为它的地位就是一个局部变量
在这里插入图片描述
4、作用域:匿名内部类也只能在方法或者代码块中,并且只能使用一次
在这里插入图片描述
5、<外部其他类> - - -不能访问—<匿名内部类>(因为 匿名内部类的地位是一个局部变量)

6、如果外部类和内部类的成员属性同名的时候,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以用(外部类名 . this . 成员属性)
在这里插入图片描述在这里插入图片描述

10.3.3.4匿名内部类最佳实践

1、当匿名内部类做为实参直接传递的时候

package com.xioawang.InnerClass;
/*将匿名内部类当作实参传递时,前提是这个类使用的次数很少或者一次*/
public class InnerClassExerise01 {
    public static void main(String[] args) {
        //传统方法:先创建一个类去实现接口,以及接口的方法,然后创建类对象去当作实参传递到方法中
        BB bb = new BB();
        fi(bb);
        
        //使用匿名内部类去做实参
        fi(new Ib() {//创建了一个Ib的接口匿名内部类
            @Override
            public void show() {
                System.out.println("匿名内部类...");
            }
        });
    }
    //
    public static void fi(Ib ib){//形参为接口的静态方法
        ib.show();
    }
}
interface Ib{//一个接口
    void show();
}
class BB implements Ib{
    @Override
    public void show() {
        System.out.println("传统方法...");
    }
}

总结:
使用传统的方法去做的时候,比较繁琐,得先创建一个类,类去实现接口及方法,然后创建类对象,当作实参去传递方法;而匿名内部类的本质也是一个类和对象,所以可以通过直接用匿名内部类去当作实参传递,前提是,这个类所使用的次数很少。

============================
2、有一个铃声的接口Bell,里面有个ring方法;有一个手机类Cellphone,具有闹钟功能alarmclock,参数是Bell类型;测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床…;再传入另一个匿名内部类(对象),打印:小伙伴上课…

package com.xioawang.InnerClass;

public class InnerClassExercise02 {
    public static void main(String[] args) {
        Cellphone cellphone = new Cellphone();//创建一个手机对象
        cellphone.alarmclock(new Bell() {
            //用匿名内部类去做实参去调用方法
            //编译类型是Bell接口,运行类型是 InnerClassExercise02$1 
            @Override
            public void ring() {
                System.out.println("懒猪起床..");
            }
        });
        cellphone.alarmclock(new Bell() {
            @Override
            public void ring() {
                System.out.println("小伙伴上课...");
            }
        });
    }
}
interface Bell{//铃声接口
    void ring();//ring方法
}
class Cellphone{
    public void alarmclock(Bell bell){//形参是一个接口类型
      bell.ring();//返回接口的ring方法,动态绑定了
    }
}
输出:
懒猪起床..
小伙伴上课...

分析下代码:
前提:接口、类这些准备工作准备好后
1、题目要求测试手机类,所以我们先创建一个手机对象

2、其次要求每一个实参传入就输出相应的铃声,说明这个铃声是只用一次的,所以用匿名内部类即可,就不用再去将手机类去实现接口,因为实现接口的话,一定要把接口的方法给实现了,那么可以说这个方法就已经具体了,虽然后面可以重写,但是代码还是会有冗余

3、手机类的方法所需要传入的实参是接口对象,那么可以使用匿名内部类去实现,再匿名内部类中去重写接口的 ring()方法,在这个地方,ring方法的编译类型是 接口 Bell ,但是运行类型不是手机类,而是 内部类所在的外部类的 外部类名 $ 1,所以,输出的不可能再是手机类里面的 ring()方法,而是外部类里面的匿名内部类的 ring()方法

10.3.4 成员内部类

基本介绍:
成员内部类是定义在外部类的成员位置的,并且没有 static 修饰
在这里插入图片描述
说明:
1、成员内部类可以访问外部类的所有成员,包括私有的(因为成员内部类的位置就是外部类的成员,所以我们要去调用实现它的话,可以在外部类中写一个方法,然后在方法中去创建成员内部类的对象,再通过这个对象去调用成员内部类的方法、成员)
在这里插入图片描述
2、可以添加任意的访问修饰符(public、private、protected、默认),因为它的地位就是外部类的一个成员
在这里插入图片描述
3、作用域:和外部类的其他成员一样,在整个类体中可以使用;比如在前面案例中,在外部类的成员方法中创建成员内部类对象,再去调用方法。
在这里插入图片描述
4、成员内部类 可以直接访问 外部类 的所有成员,包括私有的
在这里插入图片描述
5、外部类 访问 成员内部类(先创建对象,再去访问)
在这里插入图片描述
6、外部其他类 可以访问 其他类,两种访问方式(既是:先创建一个外部类的对象,然后在外部类对象的基础上,再去创建一个成员内部类的对象)

public class MenberInnerClass01 {
    public static void main(String[] args) {
        Outer06 outer06 = new Outer06();
        //外部其他类访问成员内部类的方法
        //1、outer06.new Inner06(); outer06是外部类的一个实例对象
        // 相当于把 new Inner06() 当作是outer06的成员,用outer06对象去调用new Inner06()成员
        Outer06.Inner06 inner06 = outer06.new Inner06();
        inner06.say();
        //2、在外部类中去写一个方法,然后返回 Inner06对象
        Outer06.Inner06 inner061 = outer06.getInner06();//用一个内部类去接收outer06方法的返回对象
        inner061.say();//再用inner061对象去调用方法
    }
}
class Outer06{//外部类
    class Inner06{//成员内部类
        public void say(){//是可以直接访问外部类的成员属性的,包括私有的
        }
    }
    public Inner06 getInner06(){//编写一个方法去返回一个成员内部类对象
        return new Inner06();//new Inner06()相当于就是创建了一个内部类对象,然后返回
    }
}

7、如果外部类和成员内部类的成员重名的时候,内部内访问的话,默认遵循就近原则。如果想访问外部类的成员,则使用 外部类名 . this. 成员名

public class MenberInnerClass01 {
    public static void main(String[] args) {
        Outer06 outer06 = new Outer06();
        outer06.cry();
    }
}
class Outer06{//外部类
    private int n1 = 5;//外部类私有成员属性
    class Inner06{//成员内部类
        private int n1 = 10;
        public void say(){//是可以直接访问外部类的成员属性的,包括私有的
            System.out.println("n1 = "+n1);
            System.out.println("外部类的n1 = " + Outer06.this.n1);
        }
    }
    public void cry(){//通过写一个方法,在方法中去成员内部类的对象
        // 通过对象去调用我们的成员内部类方法
        Inner06 inner06 = new Inner06();
        inner06.say();
    }
}
输出:
n1 = 10
外部类的n1 = 5
10.3.5 静态内部类

基本介绍:
静态内部类是定义在外部类的成员位置上,并且有 static 修饰

说明:

1、可以直接访问外部类的所有静态成员,包括私有的,但是不能直接访问非今天非静态成员
在这里插入图片描述
2、可以添加任意的访问修饰符(public、protected、private、默认),因为它本来就是一个成员
在这里插入图片描述
3、作用域:同其他成员,是整个类体
在这里插入图片描述
4、静态内部类 - - - -访问 - - - > 外部类(比如说:静态属性),直接访问即可,注意只能是静态的
在这里插入图片描述

5、外部类 - - - 访问- - - >静态内部类 访问方式:先创建对象,再访问
在这里插入图片描述
6、外部其他类 - - -访问- - -静态内部类

package com.xioawang.InnerClass;

import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;
import org.omg.CORBA.PRIVATE_MEMBER;

public class StaticInnerClass01 {
    public static void main(String[] args) {
        Outer07 outer07 = new Outer07();
        //6、外部其他类 - - -访问- - -静态内部类
        //方式1:
        //是直接用外部类名去调用成员的方法,但是必须要遵循访问权限(private啥的就不可以)
        Outer07.Inner07 inner07 = new Outer07.Inner07();
        inner07.cry();
        //方式2 :
        //编写一个方法去返回一个静态内部类的对象实例
        Outer07.Inner07 inner071 = outer07.getInner071();
        inner071.cry();
        //方式3:
        //编写一个静态方法去返回一个静态内部类的对象实例,这种不需要去创建一个外部类对象
        Outer07.Inner07 inner072 = Outer07.getInner07();
        inner072.cry();
    }
}
class Outer07{
    private int n1 = 10;//非静态成员属性
    private static String name = "江仔";//静态成员属性
    private static void hi(){ }//静态方法
    static class Inner07{//静态内部类,也是一个成员
        // 1、可以直接访问外部类的所有静态成员,包括私有的,但是不能直接访问非今天非静态成员
        //2、可以添加任意的访问修饰符(public、protected、private、默认),因为它本来就是一个成员
        //3、作用域:同其他成员,是整个类体**
        //4、静态内部类 - - - -访问 - - - > 外部类(比如说:静态属性),直接访问即可,注意只能是静态的**
        //5、外部类 - - - 访问- - - >静态内部类  访问方式:先创建对象,再访问**
        private int sum = 1;
        public void cry(){
            System.out.println(name);
            hi();
        }
    }
    public static Inner07 getInner07(){
        return new Inner07();
    }
    public Inner07 getInner071(){
        return new Inner07();
    }
    public void ok(){
        Inner07 inner07 = new Inner07();//创建一个静态内部类的对象
        inner07.cry();//调用静态内部类的方法
    }
}
输出:
江仔
江仔
江仔

7、如果外部类和静态内部类的成员重名时,静态内部类访问时,默认就近原则,要是要访问外部类成员,那么直接用:外部类名 . 成员,但是要确保成员属性是静态的 (这里不需要加 this 是因为本来就已经是static 修饰的静态成员了,加了this完全就没有意义)

package com.xioawang.InnerClass;
public class StaticInnerClass01 {
    public static void main(String[] args) {
        Outer07.Inner07 inner07 = new Outer07.Inner07();
        inner07.cry();
    }
}
class Outer07{
    private static int n1 = 10;//非静态成员属性
    static class Inner07{//静态内部类,也是一个成员
        private int n1 = 2;
        public void cry(){
            System.out.println("n1= "+n1+"外部类的n1= " + Outer07.n1);
        }
    }
}
输出:
n1= 2外部类的n1= 10

==============================
呀,到这里呢,我们的Java基础部分就全部学完了,这个过程呢,哈哈,怎么说呢,真的有点小乏味呀,知识点又多,后面要多多巩固呀,然后整装待发下个部分了~~~~

类、接口、内部类篇幅就在这结束了吖,这是整个Java学习中的第十章哦,觉得还不错的可以查看我完整的Java笔记哦:
Java学习第二阶段(仍然在继续更新中~~~~)
Java学习第一阶段

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-06 16:04:04  更:2022-04-06 16:07:51 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 4:41:54-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码