Day37--N种内部类

Day37--N种内部类

内部类

内部类就是在一个类的内部在定义一个类,比如,A类中定义一个B类,那么B类相对A类来说就称为内部类,而A类相对B类来说就是外部类了。

  1. 成员内部类
  2. 静态内部类
  3. 局部内部类
  4. 匿名内部类

示例:

创建外部类Outer,并设置属性private int id;方法public void out System.out.println("这是外部类的方法");

在方法out的外面、Outer的里面,设置公开内部类Public Inner,内部类的方法in,输出“这是内部类的方法”

package com.liu.oop.demo10;

//Outer  外部类
public class Outer {
    private int id=10;
    public void out(){
        System.out.println("这是外部类的方法");
    }

    //Inner   这是内部类的方法
    public class Inner{
        public void  in(){
            System.out.println("这是内部类的方法");
        }
    }
}

如何在Application里面创建内部类的对象、使用内部类的方法?

package com.liu.oop;

import com.liu.oop.demo10.Outer;

public class Application {
    public static void main(String[] args) {
        //创建内部类的对象
        //1. 创建外部类的对象
        Outer outer = new Outer();
        //2.通过外部类来实例化内部类
        //outer.new Inner();
        Outer.Inner inner = outer.new Inner();
        //使用内部类的方法
        inner.in();//这是内部类的方法
    }
}

成员变量是定义在类内部、方法外部的变量。

同理,Inner类是一个成员内部类,和类的成员变量、成员方法处于同一级别。

内部类有哪些作用?

内部非静态类可以访问外部类的属性(包括私有属性)。

package com.liu.oop.demo10;

//Outer  外部类
public class Outer {
    private int id=10;
    public void out(){
        System.out.println("这是外部类的方法");
    }

    //Inner   这是内部类的方法
    public class Inner{
        public void  in(){
            System.out.println("这是内部类的方法");
        }

        //获得内部类的私有属性
        public void getId(){
            System.out.println(id);
        }
    }
}

package com.liu.oop;

import com.liu.oop.demo10.Outer;

public class Application {
    public static void main(String[] args) {
        //创建内部类的对象
        
        //1. 创建外部类的对象
        Outer outer = new Outer();
        
        //2.通过外部类来实例化内部类
        //outer.new Inner();
        Outer.Inner inner = outer.new Inner();
        
        //使用内部类的方法
        inner.getId();//10
    }
}

内部非静态类可以访问外部类的私有方法

package com.liu.oop.demo10;

//Outer  外部类
public class Outer {
    private int id=10;
    public void out1(){
        System.out.println("这是外部类的方法1");
    }
    private void out2(){
        System.out.println("这是外部类的方法2");
    }

    //Inner   这是内部类的方法
    public class Inner{
        public void  in(){
            System.out.println("这是内部类的方法");
        }

        //获得内部类的私有属性
        public void getId(){
            System.out.println(id);
        }
        
        //获得内部类的私有方法
        public  void getOut2(){
            Outer.this.out2();
        }
    }
}

详细解释:

  1. Outer.this的含义
    • 当在内部类中使用Outer.this时,Outer.this指的是外部类(Outer类)的当前实例。
    • 例如,在Inner类的callOuterOut2方法中:
public void getOut2() {
    Outer.this.out2();
}
  • Outer.this表示获取外部类Outer的当前实例。然后通过这个实例来调用外部类的out2方法。如果没有Outer.this,编译器可能会混淆,不知道你要调用的是哪个out2方法(因为内部类可能也有自己的方法,或者存在名称冲突的情况)。

比如:

  • 当内部类和外部类有同名的成员变量或方法时,使用Outer.this可以明确地访问外部类的成员。
  • 例如,如果Inner类也有一个名为id的变量,而你想访问外部类Outerid变量,你可以这样写:
public class Inner{
    private int id = 20;
    public void printIds() {
        System.out.println("Inner id: " + this.id);
        System.out.println("Outer id: " + Outer.this.id);
    }
}
  • 这里this.id访问的是内部类Innerid变量,而Outer.this.id访问的是外部类Outerid变量。

总之,Outer.this是在内部类中访问外部类当前实例的一种方式,用于明确地调用外部类的成员。

与上面相反,内部静态类不能直接调用外部类的非静态属性。

例如:将内部类属性改为static,立刻报错

package com.liu.oop.demo10;

//Outer  外部类
public class Outer {
    private int id=10;
    public void out(){
        System.out.println("这是外部类的方法1");
    }
   

    //Inner   这是内部类的方法
    public static class Inner{
        public void  in(){
            System.out.println("这是内部类的方法");
        }

        //获得内部类的私有属性
        public void getId(){
            System.out.println(id);//报错
        }

        
    }
}

image-20241123110211524

一个Java文件只能有一个public class文件(非内部类),但是可以有多个class文件(成员内部类)

package com.liu.oop.demo10;


public class Outer {

}

class A{
    
}

此时,在项目列表里面,就可以看到

image-20241123110548691

总结:成员内部类的特点有:

  • 访问权限和特点
    • 内部非静态类可以访问外部类的属性(包括私有属性)。 内部静态类不能直接调用外部类的非静态属性。
    • 外部类要访问内部类成员,需要先创建内部类对象。
    • 创建内部类对象时,必须先有外部类对象。例如,要创建Inner类对象,需要Outer outer = new Outer(); Outer.Inner inner = outer.new Inner();这样的步骤。

局部内部类:

定义位置和方式

  • 定义在方法内部、代码块内部等局部范围。
package com.liu.oop.demo10;


public class Outer {

    public void method(){
    
        class A{
            
        }
        
    }
}

  • 访问权限和特点
    • 只能在定义它的方法或代码块内部使用。它的作用域仅限于定义它的局部范围。
    • 可以访问外部类的成员变量,还可以访问定义它的方法中的局部变量(前提是局部变量是final或者是事实上的final,从 Java 8 开始,只要局部变量在初始化后没有被重新赋值,就可以被局部内部类访问)。

拓展:

没有名字初始化类,不用将类保存到变量中。

例如:创建公开类test,在test里面创建main方法。在Test里面创建一个和Test类同级的Apple类,在Apple类里面创建公开的eat方法:输出1

package com.liu.oop.demo10;

public class Test {
    public static void main(String[] args) {
       //没有名字初始化类,不用将类保存到变量中。
        new Apple().eat();
    }
    
}


class Apple{
    public void eat(){
        System.out.println("1");
    }
}

以下是对new Apple().eat();这句代码的详细解释:

好的,以下是对new Apple().eat();这句代码的详细解释:

创建对象

  • new Apple():这部分代码的作用是创建Apple类的一个新对象。在Java中,使用new关键字来实例化一个类,也就是在内存中为这个类分配空间并初始化其成员变量等。这里创建了一个Apple类的实例,就好比制造出了一个具体的“苹果对象”,它具有Apple类所定义的各种属性和行为(虽然在这个简单示例中Apple类目前只有一个行为方法eat)。

调用方法

  • .eat():在创建了Apple类的对象之后,紧接着使用.操作符来调用该对象的方法。这里调用的是Apple类中定义的eat方法。也就是说,对刚刚创建出来的这个“苹果对象”,让它执行eat的操作。当eat方法被调用时,它里面的代码就会被执行,在这个例子中,eat方法里只有一行代码System.out.println("1");,所以会在控制台输出1

综合起来,new Apple().eat();就是先创建一个Apple类的对象,然后立即调用这个对象的eat方法,以实现让这个“苹果对象”执行“吃”这个动作(在代码层面就是输出相关内容)的目的。

匿名内部类:

​ 示例:在上面代码的基础上,写下和Test、Apple平级的接口UserService:抽象方法hello。然后在Test类里面创建接口的匿名类的实例,在匿名类里面要重写hello方法

package com.liu.oop.demo10;

public class Test {
    public static void main(String[] args) {
        //没有名字初始化类,不用将类保存到变量中
        new Apple().eat();

        UserService userService=new UserService(){
            @Override
            public void hello() {

            }
        };

    }

}


class Apple{
    public void eat(){
        System.out.println("1");
    }
}

interface UserService{
    void hello();
}

对代码的解释:

  • UserService userService = new UserService() {... };
    

    这行代码中:

    • 创建接口的匿名类的实例:虽然接口本身不能被实例化(因为它没有构造函数且只定义了方法规范),但在这里通过使用匿名内部类的方式,实际上是在创建一个实现了 UserService 接口的匿名类的实例。
    • 实现接口方法:紧跟在 new UserService() 后面的大括号内的代码 { @Override public void hello() { } } 就是在实现 UserService 接口中的 hello 方法。这里虽然方法体暂时为空,但按照要求必须提供 hello 方法的具体实现,因为要创建的是 UserService 接口的一个有效实现类的实例。
    • 赋值给变量:最后将创建的这个实现了 UserService 接口的匿名类的实例赋值给变量 userService,这样就可以在后续的代码中通过这个变量来调用该匿名类实现的方法(在这里就是 hello 方法)。

接口不能初始化,但可以通过初始化实现接口的类来使用接口。

​ 例如:

    • 定义一个简单的接口AnimalSound,里面的
interface AnimalSound {
    void makeSound();
}
  • 因为接口没有具体的实现部分,所以不能像普通类一样直接进行初始化(即不能使用new关键字来创建接口的实例)。这是由于接口本身只是一个契约,规定了实现它的类必须要做什么(实现接口中的方法),而不是一个可以直接实例化的具体对象。

通过实现类来使用接口

  • 定义实现类
    • 可以创建一个类来实现接口,并且提供接口中方法的具体实现。例如,定义一个Dog类来实现AnimalSound接口:输出汪汪汪
class Dog implements AnimalSound {
    @Override
    public void makeSound() {
        System.out.println("汪汪汪!");
    }
}
  • 使用实现类来调用接口方法
    • Dog类实现了AnimalSound接口后,就可以创建Dog类的实例,并通过这个实例来调用接口中定义的方法。例如,在Application的main方法中使用:
public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.makeSound();
    }
}
  • 在这里,Dog类作为AnimalSound接口的实现类,实现了接口中的makeSound方法。通过创建Dog类的对象dog,然后使用dog.makeSound()就可以调用这个方法,实际上也就是在执行Dog类中对makeSound方法的具体实现,从而输出 “汪汪汪!”。这样就通过实现接口的类来有效地使用了接口所规定的方法。
posted @ 2024-11-23 11:28  1hahahahahahahaha  阅读(1)  评论(0编辑  收藏  举报