基本介绍
一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。是我们类的第五大成员【属性、方法、构造器、代码块、内部类】,内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系。
基本语法
class Outer{ //外部类
class Inner{ //内部类
}
}
class Other{ //外部其他类
}
内部类的分类
★定义在外部类的局部位置上(比如方法内或者代码块中)
1)局部内部类(有类名)
①可以直接访问外部类的所有成员,包含私有的;
②不能添加访问修饰符,因为是一个局部变量,局部变量时不能使用访问修饰符的。但是可以使用final修饰,局部变量可以定义为final(最终的);
③作用域:仅仅在定义它的方法中和代码块中;
④局部内部类访问外部类成员,直接访问;
⑤外部类访问局部内部类的成员,需要创建对象再访问(必须在作用域内);
⑥外部其他类不能访问局部内部类(因为局部内部类是一个局部变量)
⑦如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,可以使用(外部类名.this.成员名)来访问
package com.pero.innerclass;
/**
* 局部内部类
* 定义在外部类的局部位置,比如方法中
*
* @version 1.1
* @author Pero
*/
public class LocalInnerClass {
public static void main(String[] args) {
new Outer02().hi();
//new Inner02(); ⑥外部其他类不能访问局部内部类
}
}
class Outer02{
//属性
private String name = "jake";
//私有方法
private void say(){
System.out.println("外部类的say()方法");
}
//方法
public void hi(){
String name = "lucy";
//方法中定义局部内部类
//②不能添加访问修饰符,因为是一个局部变量,局部变量时不能使用访问修饰符的。
// 但是可以使用final修饰,局部变量可以定义为final(最终的);
//③作用域:仅仅在定义它的方法中和代码块中;
final class Inner02{ //本质还是一个类
//①可以直接访问外部类的所有成员,包含私有的;
public void ok(){
//④局部内部类访问外部类成员,直接访问;
//⑦如果外部类和局部内部类的成员重名时,默认遵循就近原则,
// 如果想访问外部类的成员,可以使用(外部类名.this.成员名)来访问
// 外部类名.this本质就是外部类的对象,即那个对象调用hi()方法,外部类名.this就代表那个对象
System.out.println("内部类name ="+name); //name = lucy
System.out.println("外部类name = "+Outer02.this.name); //name = jake
say();
}
}
//⑤外部类访问局部内部类的成员,需要创建对象在访问(必须在作用域内)。
new Inner02().ok();
class Inner03 { /* extends Inner02{*/ //Cannot inherit from final 'null'
}
}
}
2)匿名内部类(没有类名,重点)
匿名内部类是定义在外部类的局部位置,并且没有类名,同时还是一个对象
基本语法:
new 类或者接口(参数列表){
类体;
};(;号不能少)
匿名内部类的本质:分别是基于接口的、基于普通类的、基于抽象类的;
package com.pero.innerclass;
/**
* 匿名内部类的演示(基于接口、基于普通类、基于抽象类)
*
* @version 1.2
* @author Pero
*/
public class AnonymousInnerClass {
public static void main(String[] args) {
Outer04 outer04 = new Outer04();
outer04.method();
}
}
class Outer04{ //外部类
private int n = 10; //属性
public void method(){ //方法
//基于接口的匿名内部类
//1.需求:使用接口A,并创建对象
//2.传统方法,写一个类实现接口,创建对象并调用方法
// IA tiger = new Tiger();
// tiger.cry();
//3.如果多个类都要求实现接口并只调用一次,通过传统方法会调用许多类,会造成代码冗余
//4.通过匿名内部类简化开发
//5.tiger的编译类型:IA
//6.tiger的运行类型:Outer04$1(外部类名称+$+底层排序)
/*
底层:会分配 Outer04$1类
class Outer04$1 implements IA{
@Override
public void cry() {
System.out.println("老虎叫唤!");
}
}
*/
//7.jdk底层在创建匿名内部类Outer04$1,会立刻马上创建Outer04$1对象实例,并且把地址返回给tiger
//8.匿名内部类使用一次就不能再使用了:new Outer04$1();错误
// 对象可以反复使用。
IA tiger = new IA(){
@Override
public void cry() {
System.out.println("老虎叫唤!");
}
};
tiger.cry();
System.out.println("tiger匿名内部类的名称为:" + tiger.getClass());
//基于普通类的匿名内部类
//1.father的编译类型:Father
//2.father的运行类型:Outer04$2
//3.底层会创建匿名内部类
/*
class Outer04$2 extends Father{
public Outer04$2(String name){ //自己的理解
super(name);
}
@Override
public void test() {
System.out.println("father匿名内部类重写了test()方法");
}
}
*/
//4.同时也直接返回了匿名内部类Outer04$2的对象
Father father = new Father("Lucy"){
//为什么要传入参数Lucy?因为在生成Outer04$2对象实例之前必须要先生成父类对象Father
@Override
public void test() {
System.out.println("father匿名内部类重写了test()方法");
}
};
System.out.println("father匿名内部类的名称为:" +father.getClass());
father.test();
//基于抽象类的匿名内部类
//dog的编译类型是Animal
//dog的运行类型是Outer04$3
//必须重写抽象方法say();
Animal dog = new Animal(){
@Override
void say() {
System.out.println("小狗汪汪叫");
}
};
dog.say();
}
}
interface IA{ //接口
void cry(); //抽象方法
}
//class Tiger implements IA{
// @Override
// public void cry() {
// System.out.println("老虎叫唤!");
// }
//}
class Father{ //其他外部类
private String name;
public Father(String name) { //构造器
this.name = name;
}
public void test(){ //方法
}
}
abstract class Animal{
abstract void say();
}
匿名内部类的使用细节:
①匿名内部类既是一个类的定义,同时它本身也是一个对象,它既有定义类的特征,也有创建对象的特征,可以调用匿名内部类方法;
②可以直接访问外部类的所有成员,包含私有的;
③不能用访问修饰符修饰,它的地位是一个局部变量;
④作用域:仅仅在定义它的方法或者代码块中;
⑤匿名内部类直接访问外部类成员;
⑥外部其他类不能访问匿名内部类(因为匿名内部类是一个局部变量);
⑦如果外部类和匿名内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,可以使用(外部类名.this.成员名)来访问。
package com.pero.innerclass;
/**
* 匿名内部类使用细节
*
* @version 1.3
* @author Pero
*/
public class AnonymousInnerClassDetail {
public static void main(String[] args) {
Outer05 outer05 = new Outer05();
outer05.f();
System.out.println("outer05的hashCode值为:"+outer05.hashCode());
}
}
class Outer05{
private int n = 1;
public void f(){
//创建一个基于普通类的匿名内部类
Person person = new Person(){
@Override
public void hi() {
System.out.println("匿名内部类Outer05$1重写了hi()方法");
}
};
person.hi(); //动态绑定机制,运行类型是Outer05$1
//也可以直接调用
//与上面的匿名内部类完全不一样,这是一个新的内部类
//编译类型是Person,但是运行类型是Outer05$2
new Person(){
//重名属性
private int n = 10;
@Override
public void hi() {
System.out.println("匿名内部类中的属性n的值为:"+n); //重名属性调用遵循,就近原则
System.out.println("外部类中的属性n的值为:"+Outer05.this.n);
System.out.println("匿名内部类Outer05$1重写了hi()方法");
System.out.println("Outer05.this的hashCode为:"+Outer05.this.hashCode());
}
@Override
public void ok(String str) {
super.ok(str);
}
}/*.ok("jake")*/.hi(); //匿名内部类本身也是对象,可以直接调用方法
}
}
class Person{ //举例普通类,也可以是接口或者抽象类
public void hi(){
System.out.println("普通类Person的hi()方法");
}
public void ok(String str){
System.out.println("Person ok()"+str);
}
}
匿名内部类实践
匿名内部类当作实参直接传递,简洁高效
package com.pero.innerclass;
/**
* 匿名内部类的实践一:可当作实参进行直接传递,简洁高效
*
* @version 1.4
* @author Pero
*/
public class InnerClassExercise01 {
public static void main(String[] args) {
//当作实参直接传递
f(new IL() {
@Override
public void say() {
System.out.println("匿名内部类当作实参直接传递");
}
});
//传统的实现办法
f(new An());
}
//静态方法
public static void f(IL il){ //通过接口类型调用方法say
il.say();
};
}
interface IL{
void say();
}
//传统办法,创建一个类实现接口;在编程领域这种实现方式叫做硬编码
class An implements IL{
@Override
public void say() {
System.out.println("传统方法实现传递实参"); //如果更改输出结果对后续对象会造成影响
}
}
实践二:
package com.pero.innerclass;
/**
* 匿名内部类的实践二:
* 1.有一个铃声接口Bell,里面有个ring方法
* 2.有个手机类CellPhone,具有闹钟功能alarmclock,参数是Bell接口类型
* 3.测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:起床了
* 4.在传入另外一个匿名内部类(对象),打印:上班了
*
* @version 1.5
* @author Pero
*/
public class InnerClassExercise02 {
public static void main(String[] args) {
CellPhone cellPhone = new CellPhone();
cellPhone.alarmClock(new Bell() {
@Override
public void ring() {
System.out.println("起床了");
}
});
cellPhone.alarmClock(new Bell() {
@Override
public void ring() {
System.out.println("上课了");
}
});
}
}
interface Bell{
void ring();
}
class CellPhone{
public void alarmClock(Bell bell){
System.out.println("传入参数的运行类型:"+bell.getClass());
bell.ring(); //动态绑定;(自我理解:传递的匿名内部类(对象)调用的该方法都会进行重写)
}
}
★定义在外部类的成员位置上
3)成员内部类(没有static修饰)
①可以直接访问外部类的所有成员,包含私有的;
②可以添加任何访问修饰符(public、protect、默认、private),地位就是一个成员;
③作用域和外部类的其他成员一样为整个类体;
④成员内部类直接访问外部类其他成员;
⑤外部类通过创建成员内部类对象访问内部类成员;
⑥外部其他类访问成员内部类有三种;
⑦如果外部类和成员内部类的成员重名时,成员内部类访问的话,默认遵循就近原则,如果想访问外部类成员,可以通过使用(外部类名.this.成员名)去访问。
package com.pero.innerclass;
/**
* 成员内部类(没有static修饰)
*
* @version 1.6
* @author Pero
*/
public class MemberInnerClass01 {
public static void main(String[] args) {
Outer08 outer08 = new Outer08();
outer08.t();
//6.外部其他类访问成员内部类时的方式
//第一种:
//用外部类的对象创建一个内部类的对象,相当于将new Inner08()当作outer08对象的成员
// 编译类型:Outer08.Inner08,运行类型:Inner08;
Outer08.Inner08 inner08 = outer08.new Inner08();
//第二种:
//在外部类中编写一个方法,可以返回一个Inner08的对象实例
//编译类型:Outer08.Inner08,运行类型:Inner08;
Outer08.Inner08 inner08Instance= outer08.getInner08Instance();
inner08Instance.say();
//第三种:先创建一个外部类对象,在外部类对象下在创建内部类对象
//编译类型:Outer08.Inner08,运行类型:Inner08;
Outer08.Inner08 inner081 = new Outer08().new Inner08();
//自主理解(inner081引用指向的是内部类的对象地址)
}
}
class Outer08{ //外部类
private double d = 10.0;
public String name = "lucy";
public void hi(){
System.out.println("外部类的hi()方法");
}
public class Inner08{ //成员内部类 2.可以添加任何访问修饰符 3.作用域为整个外部类的类体中
private String name = "tom";
public void say(){
System.out.println("成员内部类大的say()方法"+name+d); //1.直接调用外部属性 4.直接访问外部类成员
//7.成员内部类和外部类属性重名时,调用遵守就近原则
}
}
//在外部类中编写一个方法,可以返回一个Inner08的对象实例
public Inner08 getInner08Instance(){
return new Inner08();
}
public void t(){
Inner08 inner08 = new Inner08(); //5.外部类访问内部类成员,创建对象在调用相关属性和方法
inner08.say();
System.out.println(inner08.name);
System.out.println(Outer08.this.name); //7.属性重名时如果使用外部类属性用外部类名.this.属性名称
}
}
4)静态内部类(使用static修饰);
①可以直接访问外部类的所有静态成员,并且有static修饰;
②可以添加任意访问修饰符(public、protected、默认、private),地位就是一个成员;
③作用域为整个外部类的类体,和其他成员一样;
④静态内部类直接访问外部类的所有静态成员;
⑤外部类通过创建对象在访问静态内部类成员;
⑥外部其他类访问静态内部类
⑦如果外部类和静态内部类的成员重名时,静态内部类访问的话,默认遵循就近原则,如果想访问外部类的静态成员,可以使用(外部类名.静态成员名)去访问。
package com.pero.innerclass;
/**
* 静态内部类
*
* @version 1.7
* @author Pero
*/
public class StaticInnerClass {
public static void main(String[] args) {
Outer09 outer09 = new Outer09();
outer09.hi();
//外部其他类访问静态内部类:
//方式一:
//静态内部类是可以通过外部类类名直接访问(前提是满足访问权限)
Outer09.Inner09 inner09 = new Outer09.Inner09();
inner09.say();
//方式二:
//在外部类成员位置编写一个非静态方法,通过创建外部类对象调用该方法,返回静态内部类的对象实例
Outer09.Inner09 inner091 = new Outer09().getInner09();
//在外部类成员位置编写一个静态方法,用外部类直接调用静态方法
Outer09.Inner09 inner09_ = Outer09.getInner09_();
}
}
class Outer09{
//私有属性
private String name = "jake";
//私有静态属性
public static double salary = 2000;
private static void cry(){}
//静态内部类,放在外部类的成员位置,使用static修饰 3.作用域为整个外部类的类体,和其他成员一样
public static class Inner09{ //2.可以添加任意访问修饰符(public、protected、默认、private),地位就是一个成员
public static double salary = 3000;
public void say(){
// 遵守静态访问规则,无法访问非静态成员
//System.out.println(name); Non-static field 'name' cannot be referenced from a static context
//访问静态内部类的成员
System.out.println("静态内部类的工资为:"+salary);
//如果外部类和静态内部类的成员重名时,静态内部类访问的话,默认遵循就近原则,
// 如果想访问外部类的静态成员,可以使用(外部类名.静态成员名)去访问。
// (注意:不用添加this,因为调用的成员是static修饰的)
System.out.println("外部类的工资为:"+Outer09.salary);//1.直接访问外部类的静态成员
cry(); //4.静态内部类直接访问外部类的所有静态成员;
}
}
public void hi(){ //5.外部类通过创建对象在访问静态内部类成员;
Inner09 inner09 = new Inner09();
inner09.say();
}
public Inner09 getInner09(){ //定义一个非静态方法返回静态内部类对象
return new Inner09();
}
public static Inner09 getInner09_(){ //定义一个静态方法返回静态内部类对象
return new Inner09();
}
}