内部类
内部类分为局部内部类,匿名内部类,成员内部类,静态成员内部类
局部内部类和匿名内部类,是在外部类的方法或代码块中
成员内部类和静态成员内部类,顾名思义,就是在外部类的成员位置上,可以理解为一个属性或方法
局部内部类三个重点:
- 局部内部类的本质还是一个类
- 局部内部类的作用域(局部类使用在方法体或代码块中)
- 局部内部类相当于一个局部变量
局部内部类的使用细节及代码
局部内部类的作用域是在方法体内或代码块,出了其作用范围就不能使用
- 局部内部类不能使用public 等 修饰符修饰,但可以用 final 进行修饰
- 局部内部类可以调用外部类的任何成员
- 当内部类和外部类重名时,则遵循就近原则使用,如想调用外部类成员,则可以 类名.this.成员名
- 其他外部类不能创建局部内部类的实例对象
- 外部类访问局部内部类时,则可以通过创建实例对象来访问
public class LocalInnerClass {
public static void main(String[] args) {
Outer02 outer02 = new Outer02();
outer02.m2();
System.out.println(outer02);
}
}
class Outer02 {
{
System.out.println("Outer02 代码块");
}
private int n1 = 10;
private void m1() {
System.out.println("m1 方法");
}
public void m2() {
//1.局部内部类,不能使用修饰符进行修饰,但可以使用final
//2.局部内部类,可以使用外部类的私有成员
//4.当内部类的成员 和 外部类成员 名称一致时,遵循就近原则访问
// 当想访问外部类成员时,调用方法 外部类名.this.成员名
//
// 5.外部其他类不能访问局部内部类
class Inner02 {
String name = "局部内部类属性";
int n1 = 99;
public void hi() {
//4.当内部类的成员 和 外部类成员 名称一致时,遵循就近原则访问
// 当想访问外部类成员时,调用方法 外部类名.this.成员名
//使用细节:如果取消了类名 this.n1
// 则相当于 this 这里调用的局部类的全局变量,
// 相当于谁创建了该内部类类对象,调用了该方法。则是谁,
// 这里则是 inner02 对象,我们可以使用 hashCode值来查看
System.out.println(n1 + "外部类 n1= " + Outer02.this.n1);
m1();
//这里的哈希值 和 主方法中创建了 外部类 outer02对象的哈希值是一致的
//代表着 那个对象 调用 hi()方法,Outer02.this.n1 则就是那个对象
System.out.println("Outer02.this hashCode = " + Outer02.this);
//这里此刻的哈希值 和 inner02的哈希值是一致的
//System.out.println("hashCode = " + this);
}
}
//3.外部类访问内部类时,可以通过创建内部类实例对象来访问
Inner02 inner02 = new Inner02();
inner02.hi();
System.out.println(inner02);
}
public void m3() {
//5.其他类不能访问局部内部类
//Inner02 inner02 = new Inner02();
}
}
匿名内部类
相当于整个类被创建后,就会在堆中开辟一个对象空间,接着这个类 和对象空间,就会被JVM 隐藏在最底层。
该类的对象只能只能被创建一次,但其创建后的对象,可以赋值给其他变量
匿名内部类的语法 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(){
/* 基于接口的匿名内部接口
需求:1.想使用IA接口,并创建对象
2.传统方法,写一个类。创建对象,并调用
3.需求是Tiger/Gou类,只调用一次,后面不再使用
这种方式向上转型,编译类型是IA,运行类型是Tiger。当代码编译时,
调用的是IA中的cry方法
但因运行时,运行类型是Tiger,所以最终运行的是Tiger中的cry方法
IA tiger = new Tiger();
tiger.cry();
4.可以使用匿名内部类来简化开发
5.tiger的编译类型 ? IA
6.tiger的运行类型 ? 就是匿名内部类 Outer04$1 其实就是 外部类名 + $1
我们看底层 会分配 类名 Outer04$1 其实就是 外部类名 + $1
class XXXXX implements IA {
@Override
public void cry() {
System.out.println("老虎 吼吼吼...");
}
};
7.jdk底层在创建匿名内部类 Outer04$1, 并且立即马上创建 Outer04$1 的实例对象,
并把其地址赋值给 tiger
8.匿名内部类使用一次,就不能在使用。但其对象还可以继续使用
可以理解为匿名内部类new 该一个实例对象后,就被锁定了,不能在创建其他实例对象
但其对象还可以继续使用。可以理解不能在创建其他实例对象来调用该类中的方法
*/
/*
new IA() {
@Override
public void cry() {
System.out.println("老虎 吼吼吼...");
}
};
1.new IA() {}; = new XXXX implements IA xxxx= 外部类名&数字 -> Outer04&1
2. class Outer04&1 implements IA{
@Override
public void cry() {
System.out.println("老虎 吼吼吼...");
}
3.现在底层创建了一个匿名内部类 实现了 IA接口,接着创建了该匿名内类的实例对象,
之后JVM将其类和堆中对象空间删除
所以是只会创建其一个开辟其一个对象空间
*/
IA tiger = new IA() {
@Override
public void cry() {
System.out.println("老虎 吼吼吼...");
}
public void eat() {
System.out.println(n1);
}
};
tiger.cry();
System.out.println(tiger.getClass());
tiger.cry();
tiger.cry();
tiger.cry();
//匿名内部类
//匿名内部类和普通的创建对象的区别就在于是否有大括号冒号
//father的编译类型: Father
//father的运行类型: 外部类名 + $2(根据顺序来的,因为上面有一个1)。 Outer04$2
//现在其本质就是clss Outer04$2 extends Father;
//("jack"):可以理解为构造列表。自动传送给Father构造器(父类构造器)
// 是因为父类Father中有一个name的构造器,而这相当于
//子类构造器中一般要重写父类的构造器一样
//
Father father = new Father("jack") {
@Override
public void test() {
System.out.println("匿名内部类重写了test方法");
}
};
System.out.println("father对象的运行类型=" + father.getClass());
father.test();
}
}
interface IA {
public void cry();
}
class Father {
public Father(String name) {
System.out.println("接收到 name= " + name);
}
public void test() {
}
}
class Cat2 extends Father {
public Cat2(String name) {
super(name);
}
}
/**
* 匿名内部类的使用细节
*
* 3.匿名内部类 不能在 外部其他类创建
* 4.匿名内部类 相当于 局部变量, 所以其使用/生效的范围 就是在该方法体内 或 代码块内
* 5.外部类 不能调用其 内部类所有成员, 除了是在其作用域内
* 6.当外部类 和 内部类 成员重名时,则调用遵从的是就近原则,
* 如想要调用其外部类成员时 外部类名.this.成员名
*
* 7.当其父类 和 外部类 都有其成员重名时,则会优先调用其父类
* 如父类的因修饰符调用不了 则会调用外部类的。如 父类的n1 是 私有的时,
则会调用其外部类
* 这点和继承有所区别,继承则是父类的因其访问权限,则会直接报错,不
会向上一层的父类查找
*
* 但方法重名时,则其父类方法是其私有的,则会直接报错,而不会再调其外部类
*
*
*
*/
public class AnonymousInnerClassDetail {
public static void main(String[] args) {
Outer05 outer05 = new Outer05();
outer05.f1();
}
}
class Outer05 {
private int n1 = 88;
public void m1() {
System.out.println("外部类 m1方法");
}
public void f1() {
//匿名内部类的方法调用细节
Person p = new Person() {
@Override
public void hi() {
//2.匿名内部类 可以调用类的外部成员
//细节:当其父类 和 外部类 的属性同名时,则是优先调用父类的,
// 如父类的因修饰符调用不了 则会调用外部类的
//这点和继承有所区别,继承则是父类的因其访问权限,则会直接报错,不会向上一层的父类查找
System.out.println("匿名内部类重写了 hi方法 n1= " + n1);
}
};
p.hi();
//第二种调用方法 因其匿名内部类 本质是类
// 也是可以一个对象,所以可以直接调用
// new Person() {
// @Override
// public void hi() {
// System.out.println("hi 哈哈哈哈");
// }
// }.hi();
//方法中也可以和普通方法一样,传入参数
new Person() {
}.ok("jack");
}
}
class Person {
//刚开始这里有一个n1 我要调用,但调用失败,
// 这是因为我用的是 私有修饰符 修饰所以调用不了 如果不修饰,也可以调用
//但此时的调用 就是属于继承中的调用
private int n1 = 99;
public void hi() {
System.out.println("Person 中的 hi方法");
}
public void ok(String str) {
System.out.println(str);
}
private void m1() {
System.out.println("Person类 m1方法");
}
}