Day37--N种内部类
Day37--N种内部类
内部类
内部类就是在一个类的内部在定义一个类,比如,A类中定义一个B类,那么B类相对A类来说就称为内部类,而A类相对B类来说就是外部类了。
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类
示例:
创建外部类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();
}
}
}
详细解释:
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
的变量,而你想访问外部类Outer
的id
变量,你可以这样写:
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
访问的是内部类Inner
的id
变量,而Outer.this.id
访问的是外部类Outer
的id
变量。
总之,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);//报错
}
}
}
一个Java文件只能有一个public class文件(非内部类),但是可以有多个class文件(成员内部类)
package com.liu.oop.demo10;
public class Outer {
}
class A{
}
此时,在项目列表里面,就可以看到
总结:成员内部类的特点有:
- 访问权限和特点
- 内部非静态类可以访问外部类的属性(包括私有属性)。 内部静态类不能直接调用外部类的非静态属性。
- 外部类要访问内部类成员,需要先创建内部类对象。
- 创建内部类对象时,必须先有外部类对象。例如,要创建
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
方法的具体实现,从而输出 “汪汪汪!”。这样就通过实现接口的类来有效地使用了接口所规定的方法。