java内部类
一、基本介绍
一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。是我们类的第五大成员
类的五大成员:属性、方法、构造器、代码块、内部类
内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系
基本语法:
class Outer{ //外部类
class Inner{ //内部类
}
}
class Other{ //外部其他类
}
示意图:
基本代码演示:
// 外部其他类
public class InnerClass01 {
public static void main(String[] args) {
}
}
//外部类
class Outer{
//属性
private int n1 = 10;
//构造器
public Outer(int n1) {
this.n1 = n1;
}
//方法
public void m1(){
System.out.println("m1() 方法");
}
//代码块
{
System.out.println("外部类代码块");
}
//内部类,在Outer类的内部
class Inner{
}
}
二、内部类的四大分类
基本说明:
定义在外部类局部位置上(比如方法内):
1、局部内部类(有类名)
2、匿名内部类(没有类名,开发中用的很多)
定义在外部类的成员位置上:
1、成员内部类(没有static修饰)
2、静态内部类(使用static修饰)
1、局部内部类
说明:局部内部类是定义在外部类的局部位置,比如说方法中,并且有类名。
1、可以直接访问外部类的所有成员,包含私有的
2、不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用 final 修饰,因为局部变量也可以使用 final
3、作用域:仅仅在定义它的方法或代码块中
4、局部内部类-----访问----->外部类成员 【访问方式:直接访问】
5、外部类-----访问----->局部内部类的成员【访问方式:创建对象,再访问(注意:必须再作用域内)】
6、外部其他类------不能访问-------->局部内部类(因为局部内部类地位是一个局部变量)
7、如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
原因:
示例代码:代码中的注释就是解释
public class LocalInnerClass {
public static void main(String[] args) {
Outer02 outer02 = new Outer02();
outer02.m1();
}
}
class Outer02 {
//属性
private int n1 = 100;
private int n2 = 1;
//外部类的私有方法
private void m2(){
System.out.println("我是外部类的私有方法");
}
//方法
public void m1(){
//1、局部内部类是定义在外部类的局部位置,通常是在方法
//3、不能添加访问修饰符(public、private等),但是可以用final修饰,但是修饰过后的内部类不允许被继承
class Inner02{ //局部内部类(本质仍然是一个类)
//内部类的成员变量,与外部类成员变量同名
private int n1 = 20; //这个成员变量会最先被访问到,遵循就近原则
//2、可以直接访问外部类的所有成员,包含私有
public void f1(){ //属于局部内部类的方法
System.out.println("内部类的n1 = " + n1); //输出的是内部类的成员变量的值,因为外部类和内部类的成员变量同名
//由于同名的缘故,需要(外部类.this.属性)进行访问
//那个类调用了m1这个方法,那么内部类的this指向就是指向调用了m1方法的类
//这里是Outer02调用了m1方法,所以this指向的就是Outer02
System.out.println("外部类的n1 = " + Outer02.this.n1);
//直接访问外部类的私有成员
System.out.println("外部类的n2 = " + n2);
m2();//调用外部类的私有方法
}
}
//final关键字修饰过后的内部类是不可以被继承的
//内部类可继承其他内部类,但仅限于在它定义的方法和代码块中
class Inner03 extends Inner02{}
//4、外部类在方法中,可以创建内部类对象,然后调用方法即可
Inner02 inner02 = new Inner02();
//调用内部类的方法
inner02.f1();
}
public void m3(){
//代码块
{
//内部类中仍然可以定义内部类
class Inner04{
class Inner06{
class Inner07{}
}
}
//代码块中的内部类可以继承其他存在代码块的内部类
class Inner05 extends Inner04{}
}
}
}
记住:
1、局部内部类定义在方法中、代码块中
2、作用域在方法体内或者代码块中
3、内部类的本质其实还是一个类,内部类中仍然可以定义内部类
2、匿名内部类(重点)
说明:
(1)本质还是一个类
(2)定义在一个类的内部,本质还是一个内部类
(3)该类没有名字,名字是由系统起的
(4)匿名内部类同时还是一个对象
匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名
1、匿名内部类的基本语法
new 类或接口(参数列表){
类体
};
2、匿名内部类的语法比较奇特,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,因此可以调用匿名内部类方法
演示:
public class AnonymousInnerClassDetail {
public static void main(String[] args) {
Outer05 outer05 = new Outer05();
outer05.f1();
}
}
class Outer05{
private int n = 99;
// Outer05 方法
public void f1(){
//第一种调用方法
//创建一个基于类的匿名内部类
Person p = new Person() {
//重写 hi 方法
@Override
public void hi() {
System.out.println("匿名内部类重写了hi方法");
}
};
p.hi(); //动态绑定
//第二种调用方法
new Person(){
@Override
public void hi() {
System.out.println("我是第二种调用方法");
}
}.hi();
//匿名内部类传参
new Person(){
@Override
public void ok(String str) {
System.out.println("重写ok方法,接收的参数是 " + str);
}
}.ok("outer05穿传参");
}
}
class Person{
public void hi(){
System.out.println("person say hi~");
}
public void ok(String str){
System.out.println("person接收参数 " + str);
}
}
3、可以直接访问外部类的所有成员,包含私有的
4、不能添加访问修饰符,因为它的地位就是一个局部变量
5、作用域:仅仅在定义它的方法或者代码块中
6、匿名内部类----访问----->外部类成员 [直接访问]
7、外部其他类----不能访问---->匿名内部类(因为匿名内部类地位是一个局部变量)
8、如果外部类和匿名内部类的成员重名是,内部类访问的话,遵循就近原则,如果想访问外部类其他成员,则可以使用(外部类名.this.成员)访问
使用匿名内部类的原因:
为了简化开发,当开发中遇到有一个类指向使用一次,后面不再使用,就是用匿名内部类的方式
基于接口的匿名内部类示例代码:
public class AnonymousInnerClass {
public static void main(String[] args) {
//基于接口的匿名内部类,简化开发
Outer04 outer04 = new Outer04();
outer04.method();
}
}
class Outer04{ //外部类
private int n1 = 10; //属性
public void method(){ //方法
//问题:tiger的编译类型 :接口类型IA
//问题:tiger的运行类型:匿名内部类 Outer04$1
/*
匿名内部类底层:
xxx 是底层分配的名: Outer04$1 系统分配的名字
class xxx implements IA{
@Override
public void cry() {
System.out.println("老虎叫。。。");
}
}
*/
// jdk底层在创建匿名内部类 Outer04$1 ,立即马上创建了 Outer04$1 实例,并且把地址返回给tiger
// 匿名内部类使用一次就不能再使用了,但是对象tiger可以使用
IA tiger = new IA() {
@Override
public void cry() {
System.out.println("老虎叫。。。");
}
};
System.out.println("tiger的运行类型---> " + tiger.getClass());
tiger.cry();
}
}
//接口
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$2 匿名内部类
//底层会创建一个匿名内部类
/*
底层:
class Outer04$2 extends Father{}
*/
//同时也直接返回了匿名内部类 Outer04$2 的对象
//参数列表会传递给Father的构造器
Father father = new Father("tom") {
//匿名内部类重写test方法
@Override
public void test() {
System.out.println("我重写了test方法");
}
};
System.out.println("father对象的运行类型---> " + father.getClass());
father.test();
}
}
//类
class Father{
public Father(String name){//构造器
System.out.println("Father的--name ==> " + 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();
}
练习一:
public class InnerClassExercise01 {
public static void main(String[] args) {
//接口当做实参进行传递,通过匿名内部类重写方法
f1(new IL() {
//这里重写了接口中的show方法,实现自己的逻辑
@Override
public void show() {
System.out.println("我是接口show方法的重写");
}
});
//传统的写法
//首先有一个实现了IL的实现类,再将这个类传入 f1 这个方法
Pictrue pictrue = new Pictrue();
f1(pictrue);
}
//静态方法,方法的参数是接口类型
public static void f1(IL il){
//调用接口的方法
il.show();
}
}
// 接口
interface IL{
void show();
}
//实现 IL的类
class Pictrue implements IL{
@Override
public void show() {
System.out.println("这是一幅名画。。。");
}
}
练习二:
要求:
1、有一个铃声接口 Bell,里面有一个 ring 方法
2、有一个手机类 CellPhone,具有闹钟功能 alarmclock,参数是 Blee类型
3、测试手机类闹钟功能,通过匿名内部类(对象)作为参数,打印:闹钟响了
4、再传入另一个匿名内部类(对象),打印:上课了
实现代码:
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{
// ring 方法
void ring();
}
//手机类
class CellPhone{
// 闹钟功能,参数是 Bell 类型
public void alarmclock(Bell bell){
//调用接口的 ring 方法
bell.ring();
}
}
3、成员内部类
基本介绍:
1、成员内部类是定义在外部类的成员位置,并且没有static修饰,可以直接访问外部类的所有成员,包含私有的
2、成员内部类可以添加任一访问修饰符(public、protected、默认、private),因为它的地位就是一个成员
示例代码:
public class MemberInnerClass01 {
public static void main(String[] args) {
Outer08 outer08 = new Outer08();
outer08.m1();
}
}
class Outer08{
private int n1 = 100;
public String name = "tom";
//可以用修饰符进行修饰
/*public*/ /*protected*/ /*private*/ class Inner08{
public void say(){
System.out.println("Outer08的 n1 = " + n1 +
" Outer08的 name = " + name);
}
}
//在Outer08的 m1 方法中,实例化成员内部类 Inner08
public void m1(){
Inner08 inner08 = new Inner08();
//调用成员内部类的 say 方法
inner08.say();
}
}
3、作用域和外部类的成员一样,为整个类体,比如之前的案例,在外部类的成员方法中创建成员内部类对象,再调用方法
4、成员内部类----访问---->外部类(比如属性)[访问方式:直接访问]
public class MemberInnerClass01 {
public static void main(String[] args) {
Outer08 outer08 = new Outer08();
outer08.m1();
}
}
class Outer08{
//私有方法
private void hi(){
System.out.println("我是外部类的私有方法 hi()");
}
class Inner08{
//内部类方法
public void say(){
//调用外部类的私有方法,直接调用
hi();
}
}
//在Outer08的 m1 方法中,实例化成员内部类 Inner08
public void m1(){
Inner08 inner08 = new Inner08();
//调用成员内部类的 say 方法
inner08.say();
}
}
5、外部类-----访问---->内部类(说明)[访问方式:创建对象,再访问]
6、外部其他类---->访问----成员内部类
public class MemberInnerClass01 {
public static void main(String[] args) {
Outer08 outer08 = new Outer08();
// outer08.m1();
//外部其他类调用成员内部类的三种方式
//第一种方式
Outer08.Inner08 inner08 = outer08.new Inner08(); //new Outer08().new Inner08();
inner08.say();
//第二种方式 在外部类中编写一个方法,返回Inner08对象实例
Outer08.Inner08 getInner08 = outer08.getInner08();
getInner08.say();
}
}
class Outer08{
//私有方法
private void hi(){
System.out.println("我是外部类的私有方法 hi()");
}
class Inner08{
public void say(){
//调用外部类的私有方法,直接调用
hi();
}
}
//方式二
public Inner08 getInner08(){
return new Inner08();
}
//在Outer08的 m1 方法中,实例化成员内部类 Inner08
public void m1(){
Inner08 inner08 = new Inner08();
//调用成员内部类的 say 方法
inner08.say();
}
}
7、如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,可以使用(外部类.this.成员)去访问
public class MemberInnerClass01 {
public static void main(String[] args) {
Outer08 outer08 = new Outer08();
outer08.m1();
}
}
class Outer08{
private int n1 = 100;
class Inner08{
private int n1 = 6;
public void say(){
//就近原则
System.out.println("n1的值是内部类的n1 = " + n1);
//访问外部类重名的属性
System.out.println("外部类n1的值 = " + Outer08.this.n1);
}
}
//在Outer08的 m1 方法中,实例化成员内部类 Inner08
public void m1(){
Inner08 inner08 = new Inner08();
//调用成员内部类的 say 方法
inner08.say();
}
}
4、静态内部类
说明:静态内部类是定义在外部类的成员位置,并且有static修饰
1、可以直接访问外部类的所有静态成员,包含私有的,但是不能直接访问非静态成员
2、可以添加任一访问修饰符(public、protected、默认、private),因为它的地位就是一个成员
3、作用域:同其他的成员一样,为整个类体
示例代码:
public class StaticInnerClass01 {
public static void main(String[] args) {
Outer09 outer09 = new Outer09();
outer09.show();
}
}
class Outer09{
private int n1 = 10;
private static String name = "张三";
//1、放在外部类的成员位置
//2、使用static修饰
//4、可以添加任一访问修饰符(public、protected、默认、private),因为它的地位就是一个成员
/*public*/ /*private*/ /*protected*/ static class Inner02{
public void say(){
//3、可以直接访问外部类的所有静态成员,包含私有的,但是不能直接访问非静态成员
System.out.println("访问外部类静态成员属性:" + name);
//不能直接访问外部类的非静态成员
//System.out.println("外部类非静态成员属性:" + n1);
}
}
//5、作用域:同其他的成员一样,为整个类体
//外部类方法
public void show(){
//外部类使用内部类
new Inner02().say();
}
}
4、静态内部类----访问----外部类(比如:静态属性)[访问方式:直接访问所有静态成员]
5、外部类----访问----静态内部类 [访问方式:创建对象,再访问]
6、外部其他类-----访问----->静态内部类
public class StaticInnerClass01 {
public static void main(String[] args) {
Outer09 outer09 = new Outer09();
outer09.show();
//外部其他类访问静态内部类
//方式一 因为静态内部类,是可以通过类名直接访问(前提是满足访问权限)
Outer09.Inner02 getInner02 = new Outer09.Inner02();
getInner02.say();
//方式二 通过编写一个方法,返回静态内部类的对象实例
Outer09.Inner02 inner021 = outer09.getInner02();
inner021.say();
}
}
class Outer09{
private int n1 = 10;
private static String name = "张三";
//外部类静态方法
private static void f1(){
System.out.println("我是外部类静态方法 f1()");
}
//1、放在外部类的成员位置
//2、使用static修饰
//4、可以添加任一访问修饰符(public、protected、默认、private),因为它的地位就是一个成员
/*public*/ /*private*/ /*protected*/ static class Inner02{
public void say(){
//3、可以直接访问外部类的所有静态成员,包含私有的,但是不能直接访问非静态成员
System.out.println("访问外部类静态成员属性:" + name);
//不能直接访问外部类的非静态成员
//System.out.println("外部类非静态成员属性:" + n1);
//可以直接调用外部类静态方法
f1();
}
}
//5、作用域:同其他的成员一样,为整个类体
//外部类方法
public void show(){
//外部类使用内部类
new Inner02().say();
}
//方式二 返回静态内部类的对象实例
public Inner02 getInner02(){
return new Inner02();
}
}
7、如果外部类和静态内部类的成员重名时,静态内部类访问时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类.成员)去访问
public class StaticInnerClass01 {
public static void main(String[] args) {
Outer09 outer09 = new Outer09();
outer09.show();
}
}
class Outer09{
private static String name = "张三";
static class Inner02{
//内部类的静态属性
private static String name = "李四";
public void say(){
//就近原则
System.out.println("我是内部类的静态成员变量:" + name);
System.out.println("外部类的静态成员变量:" + Outer09.name);
}
}
//5、作用域:同其他的成员一样,为整个类体
//外部类方法
public void show(){
//外部类使用内部类
new Inner02().say();
}
}
三、小结
1、内部类有四种
局部内部类、匿名内部类(重要)、成员内部类、静态内部类
2、重点还是掌握匿名内部类的使用
语法:
new 类/接口(参数列表){
//....
};
3、成员内部类、静态内部类 是放在外部类的成员位置,本质上还是一个成员
练习:
public class TestInnerClass01 {
public static void main(String[] args) {
TestInner testInner = new TestInner();
TestInner.Inner inner = testInner.new Inner();
System.out.println("main方法中的值:" + inner.a);
}
}
class TestInner{
public TestInner(){
Inner inner01 = new Inner();
inner01.a = 10;
Inner inner02 = new Inner();
System.out.println("外部类的test方法中的值:" + inner02.a);
}
class Inner{
public int a = 5;
}
}