【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$1
class Outer04$1 implements IA{
@Override
public void cry(){
System.out.println("老虎叫..");
}
}
*/
// 匿名内部类使用一次就不能再使用,但tiger作为一个对象还可以继续使用
IA tiger = new IA() {
@Override
public 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"){
@Override
public void test() {
System.out.println("匿名内部类重写了test方法");
}
};
System.out.println("father对象的运行类型="+father.getClass()); // father对象的运行类型=class java18.innerclass.Outer04$1
father.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() {
@Override
void 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;
@Override
public 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() {
@Override
public 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 {
@Override
public 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),因为它是一个成员
- 作用域:和外部类成员一样,为整个类体
- 成员内部类访问外部类成员,直接访问
- 外部类访问成员内部类,创建对象,再访问
- 外部其他类访问成员内部类
```java
public 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();
}
} ```