目录
1、基本介绍
一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。内部类是我们类的第五大成员,内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系。
Q:类的五大成员是哪些?
A:属性、方法、构造器、代码块、内部类
2、基本语法
class Outer{ //外部类
class inner{ //内部类,在Outer类的内部
}
}
class Other{ //外部其他类
}
3、内部类(共四种)的分类:
>定义在外部类局部位置上(比如方法内)
1)局部内部类(有类名) 2)匿名内部类(无类名,重点!)
>定义在外部类的成员位置上:
1)成员内部类(无static修饰) 2)静态内部类(有static修饰)
3.1局部内部类(有类名)
局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。
1)可以直接访问外部类的所有成员,包含私有的
2)不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final 修饰, 表示不可被继承
3)作用域:仅仅在定义它的方法或代码块中
4)局部内部类---访问---->外部类的成员 [访问方式:直接访问]
5)外部类---访问---->局部内部类的成员 [访问方式:创建对象,再访问(注意:必须在作用域内)]
6)外部其他类---不能访问----->局部内部类(因为局部内部类是一个局部变量)
7)如果外部类和局部内部类的成员重名时,默认遵循就近原则。如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问,如:System.out.printIn(”外部类的n2=”+外部类名.this.n2);
Q:为什么局部变量前不能用pirvate等修饰符呢?
A:因为局部变量 本身就是 一个访问权限 的设定。 只能在局部调用,也就是说局部变量的生命周期在{}之中,出了这个方法外界是没办法访问你这个变量,所以不需要用任何修饰符修饰,比如private ,public等
//演示局部内部类的使用
/*实现流程:new了个outer,调用了m1,进入m1,在这个方法体内定义了一个局部内部类,
*定义好了之后在6.处创建对象,并调用f1方法,又回到f1中输出n1的值并调用m2.
*/
public class Local {
public static void main(String[] args){
Outer outer = new Outer();
outer.m1();
}
}
class Outer{
private int n1=100;
private void m2(){
System.out.println("Outer m2()");
} //私有方法
public void m1(){
//1.局部内部类是定义在外部类的局部位置,通常在方法中
//3.不能添加访问修饰符,但是可以使用final修饰
//4.作用域:仅仅在定义它的方法或代码块中
final class Inner{ //局部内部类(本质仍然是一个类)
//2.可以直接访问外部类的所有成员,包含私有的
private int n1=800;//局部内部类成员与外部类重名
public void f1(){
//5.局部内部类可以直接访问外部类的成员,比如下面 外部类n1 和 m2()
//7.如果外部类和局部内部类的成员重名时,默认遵循就近原则
// 如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
//Outer.this 本质就是外部类的对象,即哪个对象调用了m1,Outer.this就是哪个对象
System.out.println("n1="+n1+"外部类的n1="+Outer.this.n1);
m2();
}
}
//6.外部类在方法中,可以创建Inner对象,然后调用方法即可
Inner inner = new Inner();
inner.f1();
}
{ //代码块
class Inner01{} //作用域在代码块中
}
}
3.2匿名内部类(无类名) 重点
①本质是类 ②内部类 ③该类没有名字 ④同时还是一个对象
匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名
1)可以直接访问外部类的所有成员,包含私有的
2)不能添加访问修饰符,因为它的地位就是一个局部变量。
3)作用域:仅仅在定义它的方法或代码块中
4)匿名内部类---访问---->外部类的成员 [访问方式:直接访问]
5)外部其他类---不能访问----->局部内部类(因为匿名内部类是一个局部变量)
6)如果外部类和匿名内部类的成员重名时,默认遵循就近原则。如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问,如下:
加深理解:
“所谓匿名内部类,是指可以利用内部类创建没有名称的对象,它一步完成了声明内部类和创建该类的一个对象,并利用该对象访问到类里面的成员” 《Java程序设计基础(第五版)》——陈国君
>匿名内部类的基本语法
new类或接口(参数列表){
类体
};
>基于接口的匿名内部类
//基于接口的匿名内部类
class Outer01 { //外部类
public void method() { //方法
// 1.需求:想使用IA接口,并创建对象
// 2.传统方式:写一个类,实现该接口,并创建对象
// TA tiger = new Tiger();
// tiger.cry();
// 3.需求:Tiger类只使用一次,后面不再使用
// 4.可以使用匿名内部类来简化开发
// 5.tiger的编译类型 IA 运行类型 匿名内部类
/*
我们看底层,会分配类名 Outer01$1
class Outer implements IA{
@Override
public void cry() {
System.out.println("老虎叫唤···");
}
}
*/
// 7.jdk底层在创建匿名内部类Outer01$1,立即创建了Outer01$1实例,并把地址返回给tiger
IA tiger = new IA(){
@Override
public void cry() {
System.out.println("老虎叫唤···");
}
};
tiger.cry();
}
}
interface IA { //接口
public void cry();
}
//class Tiger implements IA{
// @Override
// public void cry() {
// System.out.println("老虎叫唤···");
// }
//}
>基于类的匿名内部类
//基于类的匿名内部类
class Outer01 { //外部类
public void method() { //方法
//1.father 编译类型 Father 运行类型 Outer01$2
//2.底层会创建匿名内部类
/*
class Outer01$2 extends Father{
@Override
public void test() {
System.out.println("匿名内部类重写了test的方法");
}
*/
//4.同时也直接返回了 匿名内部类 Outer01$2的对象
//5.注意("jack")参数列表会传递给 构造器
Father father = new Father("jack"){
@Override
public void test() {
System.out.println("匿名内部类重写了test的方法");
}
};
father.test();
//基于抽象类的匿名内部类 (必须实现抽象方法)
Animal01 animal = new Animal01(){
@Override
void eat() {
System.out.println("dddd");
}
};
}
}
class Father{
public Father(String name){ //构造器
}
public void test(){ //方法
}
}
abstract class Animal01{
abstract void eat();
}
>匿名内部类的使用
匿名内部类的语法比较奇特,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征。
class Outer05{
private int n1= 99;
public void f1(){
//创建了一个基于类的匿名内部类
Person p =new Person(){
@Override
public void hi() {
System.out.println("匿名内部类重写了hi方法");
}
};
p.hi(); //动态绑定,运行类型是 Outer05$1
//也可以直接调用,匿名内部类本身也是返回对象
new Person() {
@Override
public void hi() {
System.out.println("匿名内部类重写了hi方法");
}
}.hi();
}
}
class Person{ //类
public void hi(){
System.out.println("Person hi");
}
}
>匿名内部类当做实参直接传递(简洁高效,且更灵活)
public class InnerClassExercise01 {
public static void main(String[] args) {
//当做实参直接传递,简洁高效,且更加灵活
//1.传递的是实现了IL接口的匿名内部类
//2.重写了show()方法
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("这是一副名画...");
}
}
扩展:
动态绑定机制:
当调用对象方法时,该方法会和该对象的内存地址/运行类型有关
3.3成员内部类(无static)
成员内部类是定义在外部类的成员位置,并且没有static修饰。
1)可以直接访问外部类的所有成员,包含私有的
2)可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
3)作用域:和外部类的其他成员一样,为整个类体。在外部类的成员方法中创建成员内部类对象,再调用方法
4)成员内部类---访问---->外部类(比如:属性) [访问方式:直接访问]
5)外部类---访问------>内部类 [访问方式:创建对象,再访问]
6)外部其他类---访问---->成员内部类
//成员内部类演示
public class InnerClass {
public static void main(String[] args){
Outer08 outer08 = new Outer08();
outer08.t1();
//外部其他类,使用成员内部类的三种方式
//方式一:
//outer08.new Inner08(); 相当于把 new Inner08()当做是outer08成员 (语法)
//Outer08 outer08 = new Outer08();
Outer08.Inner08 inner08 = outer08.new Inner08();
inner08.say();
//方式二:在外部类中,编写一个方法,可以返回Inner08对象
Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
inner08Instance.say();
//方式三:(本质是方式一和方式二)
Outer08.Inner08 inner088 = new Outer08().new Inner08();
inner088.say();
}
}
class Outer08{ //外部类
private int n1 = 10;
public String name = "张三";
private void hi(){
System.out.println("hi()方法调用");
}
//1.注意:成员内部类,是定义在外部内的成员位置上
//可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员
public class Inner08{ //成员内部类
private double sal = 99.8;
private int n1 = 666;
public void say(){
//可以直接访问外部类的所有成员,包括私有的
//如果成员内部类的成员和外部类的成员重名,会遵守就近原则
//可以通过 外部类名.this.属性 来访问外部类的成员
System.out.println("n1="+n1+"Outer08的name"+ name+"外部类的n1="+Outer08.this.n1);
hi();
}
}
//方法:返回一个Inner08实例
public Inner08 getInner08Instance(){
return new Inner08();
}
//写方法
public void t1(){
//使用成员内部类
//创建成员内部类的对象,然后使用相关的属性
Inner08 inner08 = new Inner08();
inner08.say();
System.out.println(inner08.sal);
}
}
3.4静态内部类(有static)
静态内部类是定义在外部类的成员位置,并且有static修饰
1)可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
2)可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是
一个成员。
3)作用域:同其他的成员,为整个类体
4)静态内部类---访问--->外部类(比如:静态属性) [访问方式:直接访问所有静态成员]
5)外部类---访问--->静态内部类 [访问方式:创建对象,再访问]
6)外部其他类---访问--->静态内部类
7)当外部类和静态内部类的成员重名,静态内部类访问时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.成员)去访问
Q:静态变量为什么不能使用this关键字?
A:public class Student{
//定义一个静态变量,名为a
static int a;
}静态变量(static 所修饰的变量),是在程序运行之前,也就是编译阶段,分配内存。
void test() {
this.a = 1;
}而this关键字的意思是,当前对象的a,也就是说,必须要有对象才能用this。
而对象的产生,必须在程序运行时,通过new产生。
所以静态变量不能使用this关键字。
//静态内部类
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();
//静态方法
Outer10.Inner10 inner102 = outer10.getInner10_();
inner102.say();
}
}
class Outer10{ //外部类
private int n1 = 10;
private static String name = "张三";
private static void cry(){}
//Inner10就是静态内部类
//1.放在外部类的成员位置
//2.使用static修饰
//3.可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
//4.可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员
//5.作用域:同其他的成员,为整个类体
public static class Inner10{
private static String name ="仪宝";
public void say(){
//6.静态内部类---访问--->外部类(比如:静态属性) [访问方式:直接访问所有静态成员]
//8.当外部类和静态内部类的成员重名,静态内部类访问时,默认遵循就近原则
// 如果想访问外部类的成员,则可以使用(外部类名.成员)去访问
System.out.println(name+" 外部类的name="+Outer10.name);
cry();
}
}
//7.外部类---访问--->静态内部类 [访问方式:创建对象,再访问]
//此处m1为外部类的一个方法
public void m1(){
Inner10 inner10 = new Inner10();
inner10.say();
}
public Inner10 getInner10(){
return new Inner10();
}
public static Inner10 getInner10_(){
return new Inner10();
}
}
静态方法中,是不可以直接new内部类实例对象和引用外部类的成员变量的。