内部类概述
1.内部类的基础
内部类的分类:实例化内部类,静态内部类,局部内部类和匿名内部类
public class OutClass {
// 成员位置定义:未被static修饰 --->实例内部类
public class InnerClass1{
}
// 成员位置定义:被static修饰 ---> 静态内部类
static class InnerClass2{
}
public void method(){
// 方法中也可以定义内部类 ---> 局部内部类:几乎不用
class InnerClass5{
}
}
}
内部类的基本概念:一个类的内部又完整地嵌套了另一个类结构,则被嵌套的类成为内部类(inner class),嵌套其他类的类称为外部类(outer class)。
- 一个类中有五大成员:属性、方法、构造器、代码块、内部类。
- 内部类最大的特点就是可以直接访问外部类的私有属性,并且可以体现类与类之间的包含关系。
- 内部类是面向对象学习的重难点,底层源码中包含大量的内部类。
2.内部类基本语法
class Outerclass { // 外部类
class Innerclass { // 内部类
}
}
class Otherclass { // 外部其他类
}
一.实例内部类
实例化内部类成员位置定义:未被static修饰 --->实例内部类
①
实例化实例内部类:
//第一种写法
Outerclass outclass = new Outerclass();
Outerclass.InnerClass innerClass = outclass.new InnerClass();
第二种写法
Outerclass.InnerClass innerClass = new Outerclass().new InnerClass();
②
内部类成员方法能访问外部类的成员变量和方法
内部类访问外部类的成员:
class Outerclass {
public int a = 1;
private int b = 2;
public void method() {
System.out.println("外部类成员方法");
}
class InnerClass {
int c = 10;
public void methodInner() {
System.out.println(a);
System.out.println(b);
System.out.println(c);
method();
}
}
public static void main(String[] args) {
Outerclass outclass = new Outerclass();
Outerclass.InnerClass innerClass = outclass.new InnerClass();
//第二种写法
//Outerclass.InnerClass innerClass = new Outerclass().new InnerClass();
innerClass.methodInner();
}
}
③
当内部类成员变量与外部类的成员变量名字相同时应该先访问自己内部类的成员变量
若要访问外部类则需要通过:
- 外部类名称.this.同名成员名字
- 实例化外部类再访问
class Outerclass {
public int c = 100;
public void method() {
System.out.println("外部类成员方法");
}
class InnerClass {
char c = 'a';
public void methodInner() {
System.out.println(c);
method();
}
}
public static void main(String[] args) {
Outerclass outclass = new Outerclass();
Outerclass.InnerClass innerClass = outclass.new InnerClass();
//第二种写法
//Outerclass.InnerClass innerClass = new Outerclass().new InnerClass();
innerClass.methodInner();
}
}
方法一:
System.out.println(Outerclass.this.c);
方法二:
Outerclass outerclass = new Outerclass();
System.out.println(outerclass.c);
④
在内部类定义main方法不能运行
⑤
用 $ 区分内部类和外部类来命名
整体代码
class Outerclass {
int a = 1;
public int c = 100;
public void method() {
System.out.println("外部类成员方法");
}
class InnerClass {
char c = 'a';
public void methodInner() {
System.out.println(a);
method();
//访问与内部类成员变量名形同的外部类成员变量
//方法一:
Outerclass outerclass = new Outerclass();
System.out.println(c);
System.out.println(outerclass.c);
//方法二
System.out.println(Outerclass.this.c);
}
}
public static void main(String[] args) {
//实例化 实例内部类
Outerclass outclass = new Outerclass();
Outerclass.InnerClass innerClass = outclass.new InnerClass();
//第二种写法
//Outerclass.InnerClass innerClass = new Outerclass().new InnerClass();
innerClass.methodInner();
}
}
总结
- 成员内部类实质上就是一个类;同时它也是一个成员变量,因此它可以用任意的访问修饰符来修饰(public、protected、默认、private),也可以用 final 来修饰,但不用 static 修饰(用 static 修饰的成员内部类叫做静态内部类)。
- 成员内部类的作用域是整个类体;它可以直接访问外部类的所有成员,包含私有的、静态的(不需要创建外部类对象);注意:成员内部类中不能定义静态成员。
- 外部类的其他成员想访问成员内部类中的非静态成员,需要先创建成员内部类的对象,再通过该对象来访问(成员内部类中没有静态成员)。
- 外部类中,不能直接访问实例内部类中的成员,如果要访问必须先要创建内部类的对象。
- 如果外部类中的成员和成员内部类中的成员重名时,内部类访问该成员默认遵循就近原则;在成员内部类中想访问外部类的同名成员,使用 外部类名.this.成员 访问。
二.静态内部类
成员位置定义:被static修饰 ---> 静态内部类
①
实例化静态内部类
Outerclass.StaticInnerclass staticInnerclass = new Outerclass.StaticInnerclass();
②
静态内部类方法只能访问外部类的静态的成员变量和方法,非静态变量和方法不能直接访问
可以通过实例化外部类再进行访问
Outerclass outerclass = new Outerclass();
System.out.println(outerclass.a);
③
如果外部类中的成员和静态内部类中的成员重名时,内部类访问该成员默认遵循就近原则;在静态内部类中想访问外部类的同名成员,使用 外部类名.成员 访问(静态内部类中不能使用 this 关键字)
class Outerclass {
static int c = 111;
//此时外部类的成员变量必须是static修饰
static class StaticInnerclass {
static int c = 100;
public void methodStaticInner() {
System.out.println(c);
System.out.println(Outerclass.c);
}
}
}
整体代码
class Outerclass {
public int a = 1;
private int b = 2;
static int c = 111;
public void method() {
System.out.println("外部类成员方法");
}
static void method1() {
System.out.println("外部类静态成员方法");
}
static class StaticInnerclass {
int a = 100;
public void methodStaticInner() {
System.out.println(c);
method1();
//System.out.println();
//System.out.println(b); 只能访问外部类的static修饰的成员
}
}
public static void main(String[] args) {
//访问外部类的非静态方法
Outerclass outerclass = new Outerclass();
System.out.println(outerclass.a);
outerclass.method();
//实例化静态内部类
Outerclass.StaticInnerclass staticInnerclass = new Outerclass.StaticInnerclass();
staticInnerclass.methodStaticInner();
}
}
总结
- 静态内部类实质上就是一个类;同时它也是一个成员变量,因此它可以用任意的访问修饰符来修饰(public、protected、默认、private),也可以用 final 来修饰,而必须使用 static 修饰。
- 静态内部类的作用域是整个类体;它可以直接访问外部类的所有静态成员,包含私有的(不需要创建外部类对象);注意:静态内部类中可以定义非静态成员和静态成员。
- 外部类的其他成员想访问静态内部类中的非静态成员,需要先创建静态内部类的对象,再通过该对象来访问;外部类的其他成员想访问静态内部类中的静态成员,则只需要使用静态内部类名.成员 便可直接访问。
辨析实例内部类和静态内部类
区别
实例化内部类(非静态内部类)与外部类实例紧密相关。这意味着非静态内部类可以访问外部类的所有成员(包括私有成员),因为它们持有外部类的隐式引用。此外,非静态内部类可以有非静态成员(方法和属性),这使得它们能够更灵活地与外部类进行交互。然而,实例化内部类的一个限制是它们必须依赖于外部类的实例才能被创建。
静态内部类则与外部类实例没有直接关联,它们可以独立于外部类对象而存在。静态内部类可以使用static关键字来修饰,这意味着它们可以访问外部类的静态成员,但不能直接访问非静态成员。静态内部类的一个主要优势是它们可以在没有外部类实例的情况下被创建和实例化,这使得它们在某些情况下更加灵活和方便。
使用场景
- 访问权限:如果需要访问外部类的非静态成员,则实例化内部类是更好的选择。如果只需要访问外部类的静态成员,那么静态内部类可能更合适。
- 创建和实例化:如果内部类需要独立于外部类对象存在,或者不需要外部类实例即可创建,那么静态内部类更适用。
- 设计考虑:在某些情况下,为了遵循面向对象的设计原则(如单一职责原则、开闭原则等),可能更倾向于使用静态内部类来将功能模块化。
三.匿名内部类
- 匿名内部类定义在外部类的局部位置(即方法和代码块中),并且不具有类名。
①
用法一:实现接口的匿名内部类
interface IA {
public void run();
}
class Outer {
public void OuterMethod() {
//实现接口的匿名内部类
IA tiger = new IA() {
@Override
public void run() {
System.out.println("tiger用四条腿跑");
}
};//内部类创建完毕
System.out.println("tiger的运行类型=" + tiger.getClass());
tiger.run();// tiger对象 可以一直使用
tiger.run();
}
}
public class Test2 {
public static void main(String[] args) {
Outer outer = new Outer();
outer.OuterMethod();
}
}
1.需求: 想实现IA接口,并创建对象
2.传统方式,是写一个实现接口的类,实现该接口,并创建该类对象
3.可要求是:该类只是使用一次,后面再不使用;
4. 可以使用匿名内部类来简化开发
5. tiger的编译类型是: IA接口
6. tiger的运行类型是: 就是匿名内部类 Outer$1 (jdk分配的类名,不能使用)底层分配 类名 Outer$1 ,如下:
class Outer$1 implements IA { @Override public void run() { System.out.println(""); } }
7. jdk底层在创建匿名内部类 Outer$1,并立即就创建了 Outer$1 实例对象, 并且把对象地址返回给 tiger;
8. 匿名内部类 Outer$1 使用一次后,就不能再使用,但其对象 tiger可以一直使用。
用法二:继承父类的匿名内部类
继承抽象类的匿名内部类
class Father {
public Father(String name) {
System.out.println("接收到name=" + name);
}
//方法
public void test() {
}
}
// 抽象类
abstract class Animal {
abstract void eat();
}
class Outer {
public void method() {
//继承父类的匿名内部类
Father father = new Father("张三"){
@Override
public void test() {
System.out.println("重写父类的方法");
}
};
System.out.println("father对象的运行类型=" + father.getClass()); // 输出 Outer$2
father.test();
}
//继承抽象类的匿名内部类
Animal animal = new Animal() {
@Override
void eat() {
System.out.println("继承抽象类的方法");
}
};
}
public class A{
public static void main(String[] args) {
Outer outer = new Outer();
outer.method();
outer.animal.eat();
}
}
1. father编译类型 Father
2. father运行类型 Outer$2
3. 底层会创建匿名内部类,如下
class Outer$2 extends Father{ @Override public void test() { System.out.println("匿名内部类重写了test方法"); } }
4. 同时也直接返回了 匿名内部类 Outer$2的对象
5. 注意("张三") 参数列表会传递给 构造器。
②
class Fu {
int a;
public void test(){};
public Fu(int a) {
this.a = a;
System.out.println(a);
}
public void ok(String str) {
System.out.println("ok(String str) " + str);
test();//动态绑定
}
}
abstract class Animal1 {
abstract void eat1();
}
class Outerclass {
public void Out() {
Fu fu = new Fu(1){
@Override
public void test() {
System.out.println("重写父类Fu()");
}
};
fu.test();
System.out.println("Fu的运行类型" + fu.getClass());
new Fu(2) {
@Override
public void test() {
System.out.println("第二次重写父类Fu()");
}
}.ok("aaa");
}
//继承抽象类的匿名内部类
Animal1 animal1 = new Animal1() {
@Override
void eat1() {
System.out.println("继承抽象类Animal1的方法eat1()");
}
};
}
public class Test3 {
public static void main(String[] args) {
Outerclass outerclass = new Outerclass();
outerclass.Out();
outerclass.animal1.eat1();
}
}
③
public class Interface01 {
public static void main(String[] args) {
f(new A() {
@Override
public void eat() {
System.out.println("没有创建对象便成功的调用了f方法,不需要实现接口");
}
});
}
public static void f(A a){
a.eat();
}
}
interface A{
public void eat();
}
---------------------------
没有创建对象便成功的调用了f方法,不需要实现接口
④
- 匿名内部类本质上还是类,但同时它本身还是一个对象。
- 匿名内部类就是实现了接口的一个类,或者是继承了父类的一个子类;匿名内部类只会被创建一次,之后便被销毁;其对象也只会被创建一次,但是该对象可以被一直使用。
- 匿名内部类既是一个类的定义,同时本身也是一个对象,又是一个局部变量。因此它可以不返回对象地址,通过自身直接调用其内部的成员;但这样该匿名内部类的对象便只能使用一次。
- 匿名内部类的作用域只在定义它的方法或者代码块中;匿名内部类不能添加访问修饰符 和 static,可以用 final 修饰,因为它的地位就是一个局部变量。
- 匿名内部类可以直接访问外部类的所有成员,包括私有的(不需要创建外部类对象)。
- 外部类的其他成员不能直接访问匿名内部类的成员,只能通过在定义了匿名内部类的方法中用匿名内部类的对象调用其成员,然后再调用该方法,实现间接使用匿名内部类的成员。
- 外部其他类不能访问匿名内部类及其成员
- 如果外部类中成员和匿名内部类中的成员重名时,内部类访问该成员默认遵循就近原则;在匿名内部类中想访问外部类的同名成员,使用 外部类名.this.成员 访问。
四.局部内部类
局部内部类定义在外部类的局部位置(即方法和代码块中),并且具有类名
局部内部类定义在方法当中,相当于一个局部变量
作用域只在定义它的方法或者代码块中。
在内部类下实例化内部类
使用 外部类名.this.成员 访问与内部类成员变量名相同的外部成员变量
整体代码
class OutClass {
int a = 10;
public void method(){
int a = 11;
int b = 10;
// 局部内部类:定义在方法体内部
// 不能被public、static等访问限定符修饰
class InnerClass{
public void methodInnerClass(){
System.out.println(a);
//通过类名.this.成员的方式来访问与内部类成员变量名相同的外部类成员变量
System.out.println(OutClass.this.a);
System.out.println(b);
}
}
// 只能在该方法体内部使用,其他位置都不能用
InnerClass innerClass = new InnerClass();
innerClass.methodInnerClass();
}
public static void main(String[] args) {
// OutClass.InnerClass innerClass = null; 编译失败
OutClass outClass = new OutClass();
outClass.method();
}
}
总结
- 局部内部类本质上还是类;但同时又相当一个局部变量,因此不能用任何访问修饰符和 static 来修饰局部内部类,可以使用 final 修饰。
- 局部内部类的作用域只在定义它的方法或者代码块中。
- 局部内部类可以直接访问外部类的所有成员,包括私有的(不需要创建外部类对象)
- 外部类的其他成员不能直接访问局部内部类的成员,只能通过在定义了局部内部类的方法中用局部内部类的对象调用其成员,然后再调用该方法,实现间接使用局部内部类的成员
- 外部其他类不能访问局部内部类。
- 如果外部类中的成员和局部内部类中的成员重名时,内部类访问该成员默认遵循就近原则;在局部内部类中想访问外部类的同名成员,使用 外部类名.this.成员 访问(外部类名.this 就相当于外部类的对象)