「Java」八、面向对象之完结篇

发布于 2022-03-26  193 次阅读


关键字:static

当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上 的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象, 其方法才可以供外部调用。我们有时候希望无论是否产生了对象或无论产生了多少 对象的情况下,某些特定的数据在内存空间里只有一份,例如所有的中国人都有个 国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中 都单独分配一个用于代表国家名称的变量。

static可以修饰属性、方法、代码块、内部类

使用static修饰属性:静态变量/类变量

属性,按是否使用static修饰,又分为静态属性&非静态属性(局部变量)

实例变量:我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性修改。

静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过的。

说明:

①静态变量随着类的加载而加载,可以通过“类.静态变量”的方式进行调用

②静态变量的加载要早于对象的创建

③由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中

④ 类变量 实例变量

类 yes no

对象 yes yes

静态属性举例:System.out,Math.PI

在开发中如何确定一个属性是否要声明为static的?

> 属性是可以被多个对象共享的,不会随着对象的不同而不同

> 类中的常量也常常声明为static的

使用static修饰方法:静态方法

①随着类的加载而加载,可以通过“类.静态方法”的方式调用

② 静态方法 非静态方法

类 yes no

对象 yes yes

③静态方法中,只能调用静态的方法和属性;非静态方法中,既可以调用静态的属性和方法,也可以调用非静态的属性和方法

④在静态的方法内,不能使用this、super关键字。静态属性和方法前省略的是类名.属性,类名.方法。应当从生命周期的角度去理解。

在开发中如何确定一个方法是否要生命为static的?

> 操作静态属性的方法,通常设置为static的,如get、set方法

> 工具类的方法,习惯上声明为static的。比如Math、Arrays、Collections

public class CircleTest{
    public static void main(String[] args){
        Circle c1=new Circle();
        Circle c2=new Circle();
        System.out.println("c1的ID:"+c1.getId());//c1的ID:1001
        System.out.println("c2的ID:"+c2.getId());//c2的ID:1002
        System.out.println("创建圆的个数为:"+Circle.getTotal());//创建圆的个数为:2
    }
}
class Circle{
    private double redius;
    private int id;
    private static int total;
    private static int init=1001;
    //构造器
    public Circle(){
        id=init++;//实现自增长
        total++;
    }
    public Circle(){
        this();
        this.radius=radius;
    }
    //方法
    public double findArea(){
        return 3.14*radius*radius;
    }
    //get、set方法
    public double getRedius(){
        return redius;
    }
    public void setRedius(double redius){
        this.redius=redius;
    }
    public int getId(){
        return id;
    }
    public static int getTotal(){
        return total;
    }
}

内存解析:

image-20220316122152249

单例(Singleton)设计模式

设计模式

设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、 以及解决问题的思考方式。设计模免去我们自己再思考和摸索。就像是经典 的棋谱,不同的棋局,我们用不同的棋谱。”套路”

常用的设计模式有 --- 23种经典设计模式

创新型模式,共5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

结构型模式,共7种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式

行为型模式,共11种:策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式

《设计模式》1995,由“四人组(Gang of Four)”合著

所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对 某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。 如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象, 静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的

实现方法:饿汉式/懒汉式

public clss SingletonTest1{
    public static void main(Sring[] args){
        Bank bank1=Bank.getInstance();
        Bank bank2=Bank.getInstance();

    }
}
//饿汉式
class Bank{
    //1.私有化类的构造器
    private Bank(){

    }
    //2.内部创建类的对象,相当于类的一个属性
    //4.要求此对象也必须是静态的
    private static Bank instance = new Bank();
    //3.提供公共的静态的方法,返回类的对象
    public static Bank getInstance(){
        return instance;
    }
}
//懒汉式
class Order{
    //1.私有化类的构造器
    private Order(){

    }
    //2.声明当前类的对象,没有初始化
    //4.此对象也必须声明为static的
    private static Order instance=null;
    //3.声明public、static的放回当前类对象的方法
    public static Order getInstance(){
        if(instance==null){
            instance=new Order();
        }
        return instance;
    }
}
//一种简化的饿汉式
class Bank{
    private Bank(){

    }
    public static final Bank instance=new Bank();
}

区分饿汉式和懒汉式

饿汉式:缺点:对象加载时间过长;优点:线程安全

懒汉式:优点:延迟对象的创建;缺点:目前的写法点成不安全--->到多线程时再修改

单例模式的优点

由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。

典型的单例: java.lang.Runtime

image-20220316175213767

应用场景

网站的计数器,一般也是单例模式实现,否则难以同步。

应用程序的日志应用,一般都使用单例模式实现,这一般是由于共享的日志 文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。

数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库 资源。

项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置 文件数据,都生成一个对象去读取。

Application 也是单例的典型应用

Windows的Task Manager (任务管理器)就是很典型的单例模式

Windows的Recycle Bin (回收站)也是典型的单例应用。在整个系统运行过程 中,回收站一直维护着仅有的一个实例。

理解main方法的语法

public static void main(String[] args){//方法体}

  1. main方法作为程序的入口
  2. main方法也是一个普通的静态方法
  3. main方法可以作为我们与控制台交互的方式(之前:使用Scanner)

由于Java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public,又因为Java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static的,该方法接收一个String类型的数组参数,该数组中保存执行Java命令时传递给所运行的类的参数。

又因为main() 方法是静态的,我们不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员,这种情况,我们在之前的例子中多次碰到。

命令行参数用法举例

public class CommandPara {
    public static void main(String[] args) {
        for (int i = 0; i < args.length; i++) {
            System.out.println("args[" + i + "] = " + args[i]);
        }
    }
}
//运行程序CommandPara.java,后面跟参数列表,可加双引号也可以不加,都认为是String类型的参数。
/*
 * javac CommandPara.java
 * java CommandPara Tom Jerry "Shkstart" 
 */
/*
 * 输出结果:
 * args[0] = Tom
 * args[1] = Jerry
 * args[2] = Shkstart
 */

类和类的成员之四:代码块(或初始化块)

代码块的作用:用来初始化类、对象

代码块如果有修饰的话,只能使用static。由是否被static修饰,分为静态代码块和非静态代码块

代码块内部可以有输出语句

静态代码块随着类的加载而执行,而且只执行一次;非静态代码块对着对象的创建而执行,每创建一个对象,就执行一次非静态代码块

作用:静态代码块初始化类的信息,非静态代码块在创建对象时对对象属性进行初始化

一个类中可以定义多个静态/非静态代码块,按声明的先后顺序执行。一般不造多个代码块。

非静态的属性和方法可以在静态和非静态的代码块中调用,静态的属性和方法只能在静态代码块中调用。

image-20220317124721725

属性赋值的先后顺序(完结)

①默认初始化

②显示初始化 / ⑤在代码块中赋值

③构造器初始化

④有了对象以后,通过“对象.属性”,“对象.方法“的方式,进行赋值

例子:由父及子,静态现行

package com.atguigu.java;
class Root{
    static{
        System.out.println("Root的静态初始化块");
    }
    {
        System.out.println("Root的普通初始化块");
    }
    public Root(){
        System.out.println("Root的无参数的构造器");
    }
}
class Mid extends Root{
    static{
        System.out.println("Mid的静态初始化块");
    }
    {
        System.out.println("Mid的普通初始化块");
    }
    public Mid(){
        System.out.println("Mid的无参数的构造器");
    }
    public Mid(String msg){
        //通过this调用同一类中重载的构造器
        this();
        System.out.println("Mid的带参数构造器,其参数值:"
            + msg);
    }
}
class Leaf extends Mid{
    static{
        System.out.println("Leaf的静态初始化块");
    }
    {
        System.out.println("Leaf的普通初始化块");
    }   
    public Leaf(){
        //通过super调用父类中有一个字符串参数的构造器
        super("尚硅谷");
        System.out.println("Leaf的构造器");
    }
}
public class LeafTest{
    public static void main(String[] args){
        new Leaf(); 
        //new Leaf();
    }
}
/*
输出:
Root的静态初始化块
Mid的静态初始化块
Leaf的静态初始化块
Root的普通初始化块
Root的无参数的构造器
Mid的普通初始化块
Mid的无参数的构造器
Mid的带参数构造器,其参数值:尚硅谷
Leaf的普通初始化块
Leaf的构造器
*/

关键字:final

在Java中声明类、变量和方法时,可使用关键字final来修饰,表示“最终的”。

final标记的类:不能被继承。提高安全性,提高程序的可读性。比如:String类、System类、StringBuffer类

final标记的方法:不能被子类重写。比如:Object类中的getClass()。

final标记的变量:(成员变量或局部变量)即称为常量。名称大写,且只能被赋值一次。

final修饰属性:可以考虑赋值的位置有:显示初始化、代码块中初始化、构造器中初始化

final修饰局部变量:尤其是final修饰形参时,表明此形参是个常量,调用此方法时给形参赋值,此后只能在方法体内使用该形参,不能赋值。(引用型变量是否可以赋值?)

static final用来修饰属性:全局常量

在开发中,通常对于自己定义的方法不适用final,对于常量则会使用final

关键字:native*

调用底层的C或C++代码来实现,不会用java来实现,因此不会对外开放

public final native Class<?> getClass;

抽象类与抽象方法(关键字:abstract)

抽象类:随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。

> 抽象类不能实例化

> 抽象类中一定有构造器,便于子类实例化时调用

> 开发中,都会提供抽象类的子类,让子类实例化,完成相关操作

抽象方法:public abstract void eat();

> 只有方法的声明,没有方法体

> 包含抽象方法的类一定是一个抽象类,反之抽象类中可以没有抽象方法

> 若子类重写了父类的抽象方法,此子类才可以实例化

若子类没有重写父类中的所有抽象方法,则此子类也应是一个抽象类

abstract使用上的注意点:

abstract不能用来修饰:属性、构造器等结构

abstract不能用来修饰private私有方法、static静态方法、final的方法、final的类

匿名子类

public static void main(String args[]){
    //创建了一匿名子类的对象:p
    Person p=new Person(){
        //如果子类只用一次,则不需要单独创建一个子类,直接使用匿名子类,名字仍用父类的类名
        //需要将父类中的抽象方法全部重写
        @Override
        public void eat(){
            System.out.println("吃东西");
        }
        @Override
        public void breath(){
            System.out.println("好好呼吸");
        }
    }
    method1(p);
    System.out.println("********************")
    //创建匿名子类的匿名对象
    method1(new Person(){
        @Override
        public void eat(){
            System.out.println("吃东西");
        }
        @Override
        public void breath(){
            System.out.println("好好呼吸");
        }
    });
}
public static void method1(Person p){//在抽象类Person中的eat和breath方法都是抽象方法。
    p.eat();
    p.breath();
}

模板方法设计模式(TemplateMethod)

解决的问题:当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用, 这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。

例子:

public class TemplateTest{
    public static void main(String[] args){
        SubTemplate t=new SubTemplate();
        t.getTime();
    }
}
abstract class Template {
    //计算某段代码花费的时间
    public final void getTime() {
        long start = System.currentTimeMillis();
        code();//不确定的部分、易变的部分
        long end = System.currentTimeMillis();
        System.out.println("执行时间是:" + (end - start));
    }
    public abstract void code();
}
class SubTemplate extends Template {
    @Override
    public void code() {
        for (int i = 0; i < 10000; i++) {
            System.out.println(i);
        }
    }
}

例题:

package com.atguigu.java;
//抽象类的应用:模板方法的设计模式
public class TemplateMethodTest {

    public static void main(String[] args) {
        BankTemplateMethod btm = new DrawMoney();
        btm.process();

        BankTemplateMethod btm2 = new ManageMoney();
        btm2.process();
    }
}
abstract class BankTemplateMethod {
    // 具体方法
    public void takeNumber() {
        System.out.println("取号排队");
    }

    public abstract void transact(); // 办理具体的业务 //钩子方法

    public void evaluate() {
        System.out.println("反馈评分");
    }

    // 模板方法,把基本操作组合到一起,子类一般不能重写
    public final void process() {
        this.takeNumber();

        this.transact();// 像个钩子,具体执行时,挂哪个子类,就执行哪个子类的实现代码

        this.evaluate();
    }
}

class DrawMoney extends BankTemplateMethod {
    public void transact() {
        System.out.println("我要取款!!!");
    }
}

class ManageMoney extends BankTemplateMethod {
    public void transact() {
        System.out.println("我要理财!我这里有2000万美元!!");
    }
}

模板方法设计模式是编程中经常用得到的模式。各个框架、类库中都有他的影子,比如常见的有:

  • 数据库访问的封装
  • Junit单元测试
  • JavaWeb的Servlet中关于doGet/doPost方法调用
  • Hibernate中模板程序
  • Spring中JDBCTemlate、HibernateTemplate等

接口(interface)

一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方 法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。

另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有is-a的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打 印机、扫描仪、摄像头、充电器、MP3机、手机、数码相机、移动硬盘等都支持USB连接。

接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要...则 必须能...”的思想。**继承是一个"是不是"的关系,而接口实现则是 "能不能" 的关系。 **

接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都 要遵守。

接口的使用:

1、接口使用interface来定义。

2、在Java中,接口和类是并列的两个结构。

3、如何定义接口:接口中的成员

JDK7及以前:只能定义全局常量和抽象方法

> 全局常量:public static final的,但是书写中可以省略不写

> 抽象方法:public abstract的

JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法(略)

interface Flyable{
    //全局常量
    public static final int MAX_SPEED=7900;
    int MIN_SPEED=1;//省略了public static final
    //抽象方法
    public abstract void fly();
    void stop();//省略了public abstract
}

4、接口中不能定义构造器!意味着接口不可以实例化

5、Java开发中,接口通过让类去实现(inpements)的方式来使用

如果实现类实现了接口中的所有抽象方法,则此类就可以实例化,否则此类认为一个抽象类

6、Java类可以实现多个接口--->弥补了Java单继承性的局限性

格式:class AA extends BB implements CC,DD,EE

//Flyable接口见上文
interface Atteckable{
    void attack();
}
class Plane implements Flyable{
    @Override
    public void fly(){
        System.out.println("通过引擎起飞");
    }
    @Override
    public void stop(){
        System.out.println("驾驶员减速停止");
    }
}
class Bullet extends Object implements Flyable,Attackable{
    @Override
    public void attack(){}
    @Override
    public void fly(){}
    @Override
    public void stop(){}
}

7、接口和接口之间可以继承,而且可以多继承

interface AA{

}
interface BB{

}
interface CC extends AA,BB{

}

8、接口的使用,体现多态性

9、接口,实际上可以看做是一种规范

项目的具体需求是多变的,我们必须以不变应万变才能从容开发。此处的“不变”就是“规范”。因此,我们开发大项目往往都是“面向接口编程”。

//体验多态性和规范
public class USBTest{
    public static void main(String[] args){
        Computer com=new Comtuper();
        //1.创建接口的非匿名实现类的非匿名对象
        Flash flash = new Flash();
        com.transferData(flash);

        //2.创建接口的非匿名实现类的匿名对象
        com.transferData(new Printer());

        //3.创建接口的匿名实现类的非匿名对象
        USB phone=new USB(){
            @Override
            public void start(){
                System.out.println("手机开始工作");
            }
            @Override
            public void stop(){
                System.out.println("手机结束工作");
            }
        };
        com.transferData(phone);

         //4.创建接口的匿名实现类的匿名对象
        com.transferData(new USB(){
            @Override
            public void start(){
                System.out.println("mp3开始工作");
            }
            @Override
            public void stop(){
                System.out.println("mp3结束工作");
            }
        });
    }
}

class Computer{
    public void transferDta(USB usb){//USB sub=new Flash
        usb.start();
        System.out.println("具体传输数据的细节");
        usb.stop();
    }
}

interface USB{
    //常量:定义了长、宽、最大最小的传输速度等
    void start();
    void stop();
}

class Flash implements USB{
    @Override
    public void start(){
        System.out.println("U盘开启工作");
    }
    @Override
    public void end(){
        System.out.println("U盘结束工作");
    }
}

class Printer implements USB{
    @Override
    public void start(){
        System.out.println("打印机开启工作");
    }
    @Override
    public void end(){
        System.out.println("打印机结束工作");
    }
}

应用举例:JDBC接口

image-20220324131422802

接口的应用:代理模式

概述: 代理模式是Java开发中使用较多的一种设计模式。代理设计就是为其他对象提供一种代理以控制对这个对象的访问。

应用场景:

安全代理:屏蔽对真实角色的直接访问

远程代理:通过代理类处理远程方法调用(RMI)

延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象

比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有 100MB,在打开文件时,不可能将所有的图片都显示出来,这样就可以使用代理 模式,当需要查看图片时,用proxy来进行大图片的打开。

分类

静态代理(静态定义代理类) 针对每一套接口都需要定义一套代理类

动态代理(动态生成代理类) JDK自带的动态代理,需要反射等知识

image-20220324134926691

public class NetWorkTest{
    public static void main(String[] args){
        //造代理类的对象,把被代理类传进去
        Server server=new Server();
        ProxyServer proxyServer=new ProxyServer();

        proxyServer.browse();
    }
}

interface NetWork{
    publiv void browse();
}

//被代理类
class Server implements NetWork{
    @Override
    public void browse(){
        System.out.println("真是的服务器访问网路");
    }
}

//代理类
class ProxyServer implements NetWork{
    private NetWork work;

    public ProxyServer(NetWork work){//通过构造器把属性初始化
        this.work=work;
    }

    public void check(){
        System.out.println("联网之前的检查工作");
    }

    @Override
    public void browse(){
        check();
        work.browse();
    }
}

工厂设计模式

包括两种设计模式:工厂方法模式、抽象工厂模式

详见 : 《拓展:工厂设计模式.doc》

Java8中接口的改进

Java 8中,你可以为接口添加静态方法和默认方法。从技术角度来说,这是完全合法的,只是它看起来违反了接口作为一个抽象定义的理念。

静态方法:使用 static 关键字修饰。可以通过接口直接调用静态方法,并执行其方法体。实现类继承不到该方法,也不能使用该方法,“完全无视”。

我们经常在相互一起使用的类中使用静态方法。你可以在标准库中找到像Collection/Collections或者Path/Paths这样成对的接口和类。

默认方法:默认方法使用 default 关键字修饰。可以通过实现类对象来调用。 如果实现类如果重写了接口中的默认方法,调用时调用的是重写的方法。

如果类继承的父类和实现的接口中,声明了同名同参数的默认方法,那么在没有重写此方法的情况下,默认调用的是父类中的方法。(属性则不适用,如果调用父类和接口中同名同参数的属性,则编译不通过。所以必须用"super."和"接口."显示的区分)--->类优先原则

如果实现类实现了多个接口,而这多个接口中声明了同名同参数的默认方法,那么在子类没有重写此方法的情况下,报错。(如果重写,则同时覆盖多个接口中的方法)--->接口冲突

我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。 比如:java 8 API中对Collection、List、Comparator等接口提供了丰富的默认 方法。

public static void method1(){

}
public default void method2(){

}
//其中public都可以省略,省略后仍是public的

如何在子类或实现类的方法中,调用父类、接口中被重写的方法?

//调用自己定义的重写的方法
method3();
//调用父类中声明的方法
super.method3();
//调用接口中的默认方法
CompareA.super.method3();//接口名.super.方法名

练习:接口冲突的解决方式(一个绝绝子的例子)

interface Filial {// 孝顺的
    default void help() {
        System.out.println("老妈,我来救你了");
    }
}
interface Spoony {// 痴情的
    default void help() {
        System.out.println("媳妇,别怕,我来了");
    }
}
class Man implements Filial, Spoony {
    @Override
    public void help() {
        System.out.println("我该怎么办呢?");
        Filial.super.help();
        Spoony.super.help();
    }
}

类的成员之五: 内部类

当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内 部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使 用内部类。

Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类。

内部类的分类:成员内部类(静态、非静态) vs. 局部内部类(方法内、代码块内、构造器内)

//内部类如果要调用外部类的非静态属性,可以直接调用,或
Person.this.eat();//外部类.this.外部类方法

//内部类方法内调用属性
//方法的形参
System.out.println(name);
//内部类的属性
System.out.println(this.name);
//外部类的属性
System.out.println(Person.this.name);

成员内部类:

一方面,作为外部类的成员:

​ > 调用外部类的结构

​ > 可以被static修饰

​ > 可以被4种不同的权限修饰

另一方面,作为一个类:

​ > 类内可以定义属性、方法、构造器等

​ > 可以被final修饰,表示此类不能被继承

​ > 可以被abstract修饰

ps.别忘了,外部类不能被static修饰(static只修饰类内部的成员,非构造器),内部类可以被static修饰;外部类不能被全部4种权限修饰

关注如下3个问题

  1. 如何实例化成员内部类的对象
public static void main(String[] args){
    //创建静态的成员内部类
    Person.Dog dog=new Person.Dog();
    dog.show();//内部类的方法
    //创建非静态的成员内部类
    Person p=new Person();
    Person.Bird bird=p.new Bird();
}
  1. 如何在成员内部类中调用外部类的结构
  2. 开发中局部内部类的使用
//返回一个实现了Comparable接口的类的对象
//方式一
public Comparable getComparable(){
    //创建一个实现了Comparable接口的类:局部内部类
    class MyComparable implements Comparable{
        @Override
        public int compareTo(Object o){
            return 0;
        }
    }
    return new MyComparable();
}
//方式二:匿名内部类的匿名对象
return new Comparable(){
    @Override
    public int compareTo(Object o){
        return 0;
    }
};

小注意点:在局部内部类的方法中(比如:show)如果调用局部内部类所声明的方法(比如:method)中的局部变量(比如:num)的话,要求此局部变量声明为final的。“复本”

jdk 7及之前的版本:要求此局部变量显式的声明为final的

jsk 8及之后的版本:可以省略final的声明

public class InnerclassTest{//外部类
    public void method(){
        int num=10;
        class AA{//局部内部类
            pulic void show(){
                System.out.println(num);
            }
        }
    }
}

应用场景:安卓开发

image-20220326101928363

总结:

成员内部类和局部内部类,在编译以后,都会生成字节码文件。

格式:

​ 成员内部类:外部类$内部类名.class

​ 局部内部类:外部类$数字 内部类名.class