【Java笔记】18 内部类
一、基本介绍
一个类的内部完整的嵌套了另一个类结构,被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。是类的五大成员(属性、方法、构造器、代码块、内部类)之一
内部类最大的特点就是可以直接访问私有属性,并可以体现类与类之间的包含关系
- 基本语法
class Outer{ //外部类
class Inner{ //内部类
}
}
class Other{ //外部其他类
}
public class InnerClass01 { // 外部其他类public static void main(String[] args) {}}class Outer{ // 外部类private int n1 = 100; // 属性public Outer(int n1) { // 构造器this.n1 = n1;}public void m1(){ // 方法System.out.println("m1()");}{ // 代码块System.out.println("代码块");}class Inner{ // 内部类}}
二、分类
- 定义在外部类局部位置上(比如方法内):
- 局部内部类(有类名)
- 可以直接访问外部类的所有成员,包含私有的
- 不能添加访问修饰符,但可以用final修饰,加final修饰后,不能被继承
- 作用域:定义它的方法或代码块中
- 局部内部类可以直接访问外部类的成员
- 外部类访问局部内部类的成员,创建对象,再访问
- 外部其他类不能访问局部内部类(因为局部内部类是一个局部变量)
- 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,可以使用 外部类名.this.成员
- 局部内部类(有类名)
/*** 局部内部类*/public class LocalInnerClass { // 外部其他类public static void main(String[] args) {Outer02 outer02 = new Outer02();outer02.m1();}}class Outer02 { // 外部类private int n1 = 100;private void m2() {System.out.println("Outer02 m2()");} // 私有方法public void m1() { // 方法// 1. 局部内部类是定义在外部类的局部位置,通常在方法// 3. 不能添加访问修饰符,但可以用final修饰final class Inner02 { // 局部内部类// 2. 可以直接访问外部类的所有成员,包含私有的private int n1 = 900;public void f1(){// 局部内部类可以直接访问外部类的成员// 如果外部类和局部内部类的成员重名时,遵循就近原则,如果想访问外部类的成员,使用外部类名.this.成员// Outer02.this本质就是外部类的对象,即哪个对象调用了m1,Outer02.this就是哪个对象System.out.println("n1=" + n1 + " 外部类的n1=" + Outer02.this.n1);m2();}}// Inner02加final修饰,不能被继承// class Inner03 extends Inner02 {//// }// 外部类在方法中,可以创建Inner02对象,然后调用方法Inner02 inner02 = new Inner02();inner02.f1();}}
2.匿名内部类- 本质是类、内部类、没有名字,同时还是一个对象new 类或接口(参数列表){// 类体};
- 基于接口的匿名内部类
public class AnonymousInnerClass {public static void main(String[] args) {Outer04 outer04 = new Outer04();outer04.method();}}class Outer04{private int n1 = 10; // 属性public void method(){ // 方法// 基于接口的匿名内部类 使用IA接口并创建对象// 传统方式:写一个类,实现该接口,并创建对象// 类只使用一次,可以使用匿名内部类简化开发// tiger的编译类型是IA// tiger的运行类型是匿名内部类 Outer04$1// jdk底层在创建匿名内部类Outer04$1,立即创建了Outer04$1实例并把地址返回给了tiger/** 底层会分配类名 Outer04$1class Outer04$1 implements IA{@Overridepublic void cry(){System.out.println("老虎叫..");}}*/// 匿名内部类使用一次就不能再使用,但tiger作为一个对象还可以继续使用IA tiger = new IA() {@Overridepublic void cry() {System.out.println("老虎叫..");}};System.out.println("tiger的运行类型=" + tiger.getClass()); // 输出 tiger的运行类型=class java18.innerclass.Outer04$1}}interface IA { // 接口public void cry();}
- 基于类的匿名内部类
public class AnonymousInnerClass {public static void main(String[] args) {Outer04 outer04 = new Outer04();outer04.method();}}class Outer04{private int n1 = 10; // 属性public void method(){ // 方法// 基于类的匿名内部类// father编译类型 Father// father运行类型 Outer04$1/** 底层会创建匿名内部类Outer04$1* class Outer04$1 extends Father{* @Override* public void test() {* System.out.println("匿名内部类重写了test方法");* }* }*/// 参数列表会传递给构造器Father father = new Father("jack"){@Overridepublic void test() {System.out.println("匿名内部类重写了test方法");}};System.out.println("father对象的运行类型="+father.getClass()); // father对象的运行类型=class java18.innerclass.Outer04$1father.test();}}class Father{ // 类public Father(String name) {System.out.println("接收到名字:"+name);}public void test(){ // 方法}}
- 基于抽象类的匿名内部类
public class AnonymousInnerClass {public static void main(String[] args) {Outer04 outer04 = new Outer04();outer04.method();}}class Outer04{private int n1 = 10; // 属性public void method(){ // 方法// 基于抽象类的匿名内部类Animal animal = new Animal() {@Overridevoid eat() {System.out.println("小狗吃骨头..");}};animal.eat();}}abstract class Animal{ // 类abstract void eat();}
匿名内部类既是一个类的定义,同时本身也是一个对象,语法上既有定义类的特征也有创建对象的特征,可以调用匿名内部类方法可以直接访问外部类的所有成员,包括私有的;外部其他类不能访问匿名内部类不能添加访问修饰符作用域:在定义它的方法或代码块中外部类和匿名内部类的成员重名时,匿名内部类访问遵循就近原则,如果想访问外部类的成员,则可以使用 外部类名.this.成员
public class AnonymousInnerClassDetail {public static void main(String[] args) {Outer05 outer05 = new Outer05();outer05.f1();//外部其他类---不能访问----->匿名内部类System.out.println("main outer05 hashcode=" + outer05);}}class Outer05 {private int n1 = 99;public void f1() {//创建一个基于类的匿名内部类//不能添加访问修饰符,因为它的地位就是一个局部变量//作用域 : 仅仅在定义它的方法或代码块中Person p = new Person(){private int n1 = 88;@Overridepublic void hi() {//可以直接访问外部类的所有成员,包含私有的//如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,//默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.this.成员)去访问System.out.println("匿名内部类重写了 hi方法 n1=" + n1 +" 外部内的n1=" + Outer05.this.n1 );//Outer05.this 就是调用 f1的 对象System.out.println("Outer05.this hashcode=" + Outer05.this);}};p.hi();//动态绑定, 运行类型是 Outer05$1//也可以直接调用, 匿名内部类本身也是返回对象// class 匿名内部类 extends Person {}// new Person(){// @Override// public void hi() {// System.out.println("匿名内部类重写了 hi方法,哈哈...");// }// @Override// public void ok(String str) {// super.ok(str);// }// }.ok("jack");}}class Person {//类public void hi() {System.out.println("Person hi()");}public void ok(String str) {System.out.println("Person ok() " + str);}}
- 最佳实践 当参数传递
public class InnerClassExercise01 {public static void main(String[] args) {//当做实参直接传递,简洁高效f1(new IL() {@Overridepublic void show() {System.out.println("这是一副名画~~...");}});//传统方法f1(new Picture());}//静态方法,形参是接口类型public static void f1(IL il) {il.show();}}//接口interface IL {void show();}//类->实现IL => 编程领域 (硬编码)class Picture implements IL {@Overridepublic void show() {System.out.println("这是一副名画XX...");}}
- 定义在外部类的成员位置上:
- 成员内部类(没用static修饰)
- 可以直接访问外部类的所有成员,包括私有的
```java public class MemberInnerClass01 { public static void main(String[] args) { Outer08 outer08 = new Outer08(); outer08.t(); } }
- 可以直接访问外部类的所有成员,包括私有的
- 成员内部类(没用static修饰)
class Outer08 { private int n1 = 90; public String name = “kk”; // 可以添加任意访问修饰符 class Inner08 { // 成员内部类 public void say() { // 可以访问外部类的所有成员,包括私有 System.out.println(“n1=”+n1+” name=”+name); } } public void t(){ // 使用成员内部类 Inner08 inner08 = new Inner08(); inner08.say(); } }
- 可以添加任意访问修饰符(public、protected、默认、private),因为它是一个成员- 作用域:和外部类成员一样,为整个类体- 成员内部类访问外部类成员,直接访问- 外部类访问成员内部类,创建对象,再访问- 外部其他类访问成员内部类```javapublic class MemberInnerClass01 {public static void main(String[] args) {Outer08 outer08 = new Outer08();outer08.t();// 外部其他类使用成员内部类的方式// 1. outer08.new Inner08(); 相当于把new Inner08()当作是outer08的成员Outer08.Inner08 inner08 = outer08.new Inner08();// 2. 在外部类中,写一个方法,返回Inner08对象Outer08.Inner08 inner08Instance = outer08.getInner08Instance();}}class Outer08 {private int n1 = 90;public String name = "kk";class Inner08 { // 成员内部类public void say() {// 可以访问外部类的所有成员,包括私有System.out.println("n1="+n1+" name="+name);}}public void t(){// 使用成员内部类Inner08 inner08 = new Inner08();inner08.say();}public Inner08 getInner08Instance(){return new Inner08();}}
- 外部类和内部类成员重名时,内部类访问默认遵循就近原则,如果想访问外部类的成员,则可以使用 外部类名.this.成员
- 静态内部类(使用static修饰)
- 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
- 可以添加任意访问修饰符 public,protected,默认,private
- 作用域:整个类体
- 静态内部类访问外部类 直接访问所有静态成员
- 外部类访问静态内部类 创建对象再访问
- 外部其他类使用静态内部类
- 如果外部类和静态内部类的成员重名时,静态内部类访问的时,默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.成员)
```java public class StaticInnerClass01 { public static void main(String[] args) { Outer10 outer10 = new Outer10(); outer10.m1(); // 外部其他类使用静态内部类 // 1. 静态内部类满足权限时可以通过类名直接访问 Outer10.Inner10 inner10 = new Outer10.Inner10(); inner10.say(); // 2. 编写一个方法,可以返回静态内部类对象实例 Outer10.Inner10 inner101 = outer10.getInner10(); inner101.say(); // 3. 静态方法 Outer10.Inner10 inner102 = Outer10.getInner10_(); inner102.say(); } }
class Outer10 { // 外部类 private int n1 = 10; private static String name = “张三”; // 可以添加任意访问修饰符 public,protected,默认,private // 作用域为整个类体 static class Inner10 { // 静态内部类 public void say() { // 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员 System.out.println(name); } } public void m1(){ // 外部类访问静态内部类 创建对象再访问 Inner10 inner10 = new Inner10(); inner10.say();
}public Inner10 getInner10(){return new Inner10();}public static Inner10 getInner10_(){return new Inner10();}
} ```
