内部类
基本介绍
一个类的内部又完整的嵌套了另一个类。被嵌套的类成为内部类,嵌套其他类的类成为外部类。是我们类的五大成员(属性,方法,构造器,代码块,内部类)
内部类最大的特点就是可以直接访问私有属性,并且可以体系那类与类之间的包含关系
注意:
内部类是学习的重难点,后面看底层源码时,有大量的内部类
基本语法
class Outer{//外部类
class Inner{//内部类}
}
class Outer{//外部其他类
}
public class InnerClass01 {//外部其他类
public static void main(String[] args) {
}
}
class Outer{//外部类
private int n = 1;//属性
public void m1(){
System.out.println("m1()");
}
{
System.out.println("构造器");
}
class Cat{//内部类,在Outer类的内部类
}
}
内部类的分类
- 定义在外部类的局部位置上(比如方法内):
- 局部内部类(有类名)
- 匿名内部类(无类名,重点)
- 定义在外部类的成员位置上:
- 成员内部类(没有static修饰)
- 静态内部类(使用static修饰)
局部内部类的使用
说明:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名
-
可以直接访问外部类的所有成员,包含私有的
-
不能添加访问修饰符,因为它的地位就是一个局部变量,局部变量是不能使用修饰符的,但是可以使用final修饰,因为局部变量也可以使用final
-
作用域:仅仅在定义它的方法或代码块中
-
局部内部类——>访问——>外部类的成员[访问方式:直接访问]
-
外部类—访问—>局部内部类的成员
访问方式:创建对象,再访问(注意:必须在作用域内)
-
外部其他类—不能访问—>局部内部类(局部内部类地位是一个局部变量)
-
如果外部类和局部内部类的成员重名是。默认遵循就近原则,如果想访问外部类的成员。则可以使用(外部类。this。成员)去访问
记住:
- 局部内部类定义在方法中/代码块
- 定义域在方法体或者代码块中
- 本质仍然是一个类
/**
* 演示局部内部类的使用
*/
public class LocalInnerClass {
public static void main(String[] args) {
Outer02 outer02 = new Outer02();
outer02.m2();
}
}
class Outer02{//外部类
private int n1 = 100;
private void m1(){
System.out.println("你");
}
public void m2(){//方法
//1.局部内部类是定义在外部类的局部位置,通常在方法中
//3.不能添加访问修饰符,但是可以使用final修饰,
//4.作用域:仅仅在定义它的方法或代码块中
class Inner02{//局部内部类(本质仍是类)
private int n1 = 200;
//2.可以直接访问外部类的所有成员,包含私有的
public void f1(){
//5.局部内部类可以直接访问外部类的成员。包括私有的
//7.如果外部类和局部内部类的成员重名是。默认遵循就近原则,如果想访问外部类的成员。使用外部类.this.成员去访问
//使用外部类名.this.成员去访问
//Outer.this 本质解释外部类的对象,即那个对象调用了m1,Outer02.this就是那个对象
System.out.println("n1 ="+n1+"外部类的n="+Outer02.this.n1);
m1();
}
}
//6.外部类在方法中,可以创建Inner02对象,然后调用方法即可
Inner02 inner02 = new Inner02();
inner02.f1();
}
}
匿名内部类的使用
- 本质还是类
- 是一个内部类
- 没有名字
- 同时是一个对象
说明匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名
-
匿名内部类的基本语法
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.如果我们的需求是这个类只使用一个,后面不再使用
//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.匿名内部类使用一次后,就不能使用了
IA tiger = new IA(){
@Override
public void cry() {
System.out.println("老虎叫");
}
};
System.out.println("匿名内部类的运行类型:"+tiger.getClass());
tiger.cry();
//演示基于类的匿名内部类
//1.father的编译类型 Father
//2.father的运行类型 Outer0$2
//3.底层创建匿名内部类
/*
底层 会分配 类名 Outer04$2
class Outer04$2 implements Father{
@Override
public void test() {
System.out.println("匿名内部类重写了test方法");
}
}
*/
//4.同时也直接返回类匿名内部类Outer04$1的对象
//5.注意(“abc”)参数列表会传递给构造器
Father father = new Father("abc"){
@Override
public void test() {
System.out.println("匿名内部类重写了test方法");
}
};
System.out.println("father对象的运行类型:"+father.getClass());
father.test();
//基于抽象类的匿名内部类
Animal animal = new Animal(){
@Override
void eat() {
System.out.println("动物吃");
}
};
animal.eat();
System.out.println("animal对象的运行类型:"+animal.getClass());
}
}
interface IA{//接口
void cry();
}
class Father{
public Father(String name) {//构造器
}
public void test(){
}
}
abstract class Animal{//抽象类
abstract void eat();
}
- 匿名内部类的语法比较奇特,它即使一个类的定义,同时其本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,对前面代码分析可以看出这个特点,因此可以调用匿名内部类方法
package com.shedu.Inner;
public class AnonymousInnerClassDetail {
public static void main(String[] args) {
Outer05 outer05 = new Outer05();
outer05.f1();
}
}
class Outer05{//外部类
private int n1 = 10;
public void f1(){
//创建一个基于类的匿名内部类
Person person = new Person(){
@Override
public void hi() {
System.out.println("匿名内部类重写了Person的hi方法");
}
};
person.hi();//动态绑定,运行类型 Outer05$1
//也可以直接调用,匿名内部类本身就是返回对象
//class 匿名内部类 extends Person{}
new Person(){
@Override
public void hi() {
System.out.println("匿名内部类重写了Person的hi方法");
}
@Override
public void ok(String str) {
System.out.println("匿名内部类重写了Person的ok方法");
}
}.ok("jjj");
}
}
class Person{//类
public void hi(){
System.out.println("Person hi()");
}
public void ok(String str){
System.out.println("Person ok()"+str);
}
}
//抽象类/接口
-
可以直接访问外部类的所有成员,包含私有的
-
不能添加访问修饰符,因为它的地位就是一个局部变量,局部变量是不能使用修饰符的,但是可以使用final修饰,因为局部变量也可以使用final
-
作用域:仅仅在定义它的方法或代码块中
-
局部内部类——>访问——>外部类的成员[访问方式:直接访问]
-
外部其他类—不能访问—>匿名内部类(因为 匿名内部类地位是一个局部变量)
-
如果外部类和匿名内部类的成员重名时,匿名内部类的访问会默认最寻就近原则
package com.shedu.Inner;
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 = 10;
public void f1(){
//创建一个基于类的匿名内部类
//如果外部类和匿名内部类的成员重名时,
// 匿名内部类的访问会默认最寻就近原则,可以用外部类名.this.成员 去访问
Person person = new Person(){
@Override
public void hi() {
System.out.println("匿名内部类重写了Person的hi方法"+n1+"外部类的n1"+Outer05.this.n1);
//Outer05.this就是调用了f1的对象
System.out.println("Outer05.this hashcode = "+Outer05.this);
}
};
person.hi();//动态绑定,运行类型 Outer05$1
//也可以直接调用,匿名内部类本身就是返回对象
//class 匿名内部类 extends Person{}
new Person(){
@Override
public void hi() {
System.out.println("匿名内部类重写了Person的hi方法");
}
@Override
public void ok(String str) {
System.out.println("匿名内部类重写了Person的ok方法");
}
}.ok("kun");
}
}
class Person{//类
public void hi(){
System.out.println("Person hi()");
}
public void ok(String str){
System.out.println("Person ok()"+str);
}
}
//抽象类/接口
练习
要求:
- 有一个铃声接口Bell,里面有个ring方法
- 有一个手机类CellPhone,具有闹钟功能alarmclock,参数事Bell类型
- 测试手机类的闹钟功能,通过匿名内部类作为参数,打印:起床了
- 再传入另一个匿名内部类,打印:小伙伴们上课了
package com.shedu.Inner;
public class InnerClassExercise02 {
public static void main(String[] args) {
CellPhone cellPhone = new CellPhone();
cellPhone.alarmclock(new Bell() {
//解读:
//1.传递的事实现了B ell接口的匿名内部类
//2.重写了ring
//3.Bell bell = 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){//形参事Bell接口类型
System.out.println(bell.getClass());
bell.ring();//动态绑定
}
}
成员内部类
成员内部类的使用
说明:陈鸿源内部类事定义在外部类的成员位置,并且没有static修饰
-
可以直接访问外部类的所有成员,包含私有的
-
可以添加任意访问修饰符(public,protected,默认,private),因为他的地位就是一个成员
public class MemberInnerClass01 { public static void main(String[] args) { Outer08 outer08 = new Outer08(); outer08.t1(); } } class Outer08{//外部类 private int n1 = 10; public String name = "张三"; //成员内部类事定义再阿文i不累的成员位置上的 class Inner08{//成员内部类 public void say(){ //可以直接访问外部类的所有成员,包括私有的 System.out.println("n1 = "+n1+" name="+name); } } //写个方法 public void t1() { //使用成员内部类 Inner08 inner08 = new Inner08(); inner08.say(); } }
-
作用域和外部类的其他成员一样,为整个类体
-
成员内部类—》访问—》外部类(直接访问)
-
外部类—》访问—》内部类 (创建对象再访问)
-
外部其他类—》访问—》成员内部类
package com.shedu.Inner; public class MemberInnerClass01 { public static void main(String[] args) { Outer08 outer08 = new Outer08(); outer08.t1(); //外部其他类,使用成员内部类的三种方法 //第一种方式 //outer08.new Inner08(); 相当于new Inner08()当作是outer08成员 //这就是一个语法 Outer08.Inner08 inner08 = outer08.new Inner08(); inner08.say(); //第二种方式 //在外部类中,编写一个方法,可以返回Inner08对象 Outer08.Inner08 inner081Instance = outer08.getInner08Instance(); inner081Instance.say(); //第三种方式 Outer08.Inner08 inner081 = new Outer08().getInner08Instance(); inner081.say(); } } class Outer08{//外部类 private int n1 = 10; public String name = "张三"; //成员内部类事定义在外部类的成员位置上的 class Inner08 {//成员内部类 public void say() { //可以直接访问外部类的所有成员,包括私有的 System.out.println("n1 = " + n1 + " name=" + name); } } //方法:返回一个Inner08实例 public Inner08 getInner08Instance(){ return new Inner08(); } //写个方法 public void t1() { //使用成员内部类 //创建成员内部类的对象,然后使用相关的方法 Inner08 inner08 = new Inner08(); inner08.say(); } }
-
如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果像访问外部类的成员,则可以使用(外部类名.this.成员)去使用
静态内部类
说明:静态内部类时定义在外部类的成员位置,并且有static修饰
- 可以直接访问外部类的所有静态成员,包含私有的,但是不饿能直接访问非静态成员
- 可以添加任意访问修饰符,他的地位就是一个成员
- 作用域,同其他的成员,为整个类体
- 静态内部类—访问—》外部类(直接访问所有静态成员)
- 外部类—访问—》静态内部类 先创建对象,在访问
- 外部其他类—访问—静态内部类
- 如果外部类和静态内部类的成员重名时,静态内部类访问时,默认遵循就近原则,如果像访问外部类的成员,则可以使用(外部类名.成员)访问
public class StaticInnerClass01 {
public static void main(String[] args) {
Outer10 outer10 = new Outer10();
outer10.m1();
//外部其他类,使用静态内部类
//方式一:
//因为是静态内部类,是可以通过类名直接访问(前提是满足访问权限)
Outer10.Inner10 inner10 = new Outer10.Inner10();
inner10.say();
//方法二:
//编写一个方法,可以返回静态内部类的对象实例
System.out.println("---------");
Outer10.Inner10 inner101 = outer10.getInner10();
inner101.say();
}
}
class Outer10{//外部类
private int n1 = 10;
private static String name = "张三";
//Inner10是静态内部类
//1.放在外部类的成员位置
//2.使用static修饰
//3.可以直接访问外部类的所有静态成员,包含私有的,但是不饿能直接访问非静态成员
//4.可以添加任意访问修饰符,他的地位就是一个成员
//5.作用域,同其他的成员,为整个类体
static class Inner10{//静态内部类
private static String name = "李四";
public void say(){
// 如果外部类和静态内部类的成员重名时,静态内部类访问时,
// 默认遵循就近原则,如果像访问外部类的成员,则可以使用(外部类名.成员)访问
System.out.println(name+"外部类name:"+Outer10.name);
}
}
//6.外部类---访问---》静态内部类 先创建对象,在访问
public void m1(){
Inner10 inner10 = new Inner10();
inner10.say();
}
public Inner10 getInner10(){
return new Inner10();
}
}