类的五大成员:属性,方法,构造器,代码块和内部类
内部类
字如其名
就是在类的内部定义一个类
这个类就叫做内部类
内部类最大的特点是可以直接访问私有属性,并可以体现类与类之间的包含关系。
从种类上说,内部类可以分为四类:
首先是定义在外部类的成员位置上的普通内部类(没用static修饰)和静态内部类(使用static修饰)
然后是定义在外部类的局部位置上(比如说方法体内)的匿名内部类(无类名)、局部内部类(有类名)
我们来一个个看:
四种内部类
1.成员内部类
public class InnerClassTest {
public class InnerClassA {
}
}
在这种定义方式下,成员内部类对象依赖外部类对象而存在,即在创建一个普通内部类对象时首先需要创建其外部类对象
当然可以在方法里创建
然后调用方法也可以比如
如何创建
package abstrac;
public class Demo01 {
class A{
int a;
}
Demo01.A a=new A();//在外部类中创建内部类
}
package abstrac;
public class Demo02 implements Demo03,Demo04 {
public static void main(String[] args) {
Demo01 demo01 = new Demo01();
Demo01.A a2=demo01.new A();//在其他类中创建Demo01的内部类A
//先创建一个Demo01,然后用这个变量创建A
}
}
Demo01 demo01 = new Demo01();
Demo01.A a2=demo01.new A();
内部类对象可以访问外部类对象中所有访问权限的字段,同时,外部类对象也可以通过内部类的对象引用来访问内部类中定义的所有访问权限的字段
细节
1.可以直接使用外部类成员(包括private)
2.定义在外部的成员位置上
3.可以添加任何的访问修饰符(因为它是一个成员)
4.作用域,和其他成员一样,为整个类体
5.成员内部类访问外部类成员=>直接访问
6.外部类访问成员内部类,先创建对象后访问
7.外部其他类访问内部类,先创建外部类对象,再创建内部类对象(上面有案例哦)
public class InnerClass {
private int c=10;
public class IneerClassA{
int a;
public int getC() {//可以在类的内部使用外部类的任意修饰符成员
return c;
}
}
}
public class Show {
public static void main(String[] args) {
InnerClass innerClass = new InnerClass();
IneerClassA ineerClassA = innerClass.new IneerClassA();
System.out.println(ineerClassA.getC());//是这样的不能说直接通过对象.a访问对于的变量成员
}
}
2.静态内部类
静态内部类的定义方法
与成员内部类类似,在成员的位置上定义,只不过前面加上static
class B{
static class A{
}//A就是静态内部类
}
细节
1.放在外部类成员位置并用static修饰
2.静态类可以访问外部类静态成员(包括私有),但是不能访问非静态成员
3.可以添加任意访问修饰符
4.作用域:整个类体
5.静态内部类访问外部类(比如静态属性)=>直接访问
6.外部类访问静态内部类=>先创建对象再访问 (内部类名 引用名=new 构造器)
7.外部其他类访问静态内部类
(1)外部类名.静态内部类名 引用名=new 外部类名.构造器(下面有案例哦)
(2)编写一个普通方法返回静态内部类的对象实例(先创建外部类,再通过外部类对象调用方法)
(3)编写一个静态方法,返回对象实例(可以直接类名.方法名();调用)
8.如果外部类和静态内部类重名,也是就近原则,可以通过类名.成员名访问外部类成员
一些小理解
我们知道,一个类的静态成员独立于这个类的任何一个对象存在,只要在具有访问权限的地方,我们就可以通过 类名.静态成员名
的形式来访问这个静态成员,同样的,静态内部类也是作为一个外部类的静态成员而存在,创建一个类的静态内部类对象不需要依赖其外部类对象。例:
Demo01.A a=new Demo01.A();//A是静态内部类,Demo01是A的外部类
可以看到,静态内部类就像外部类的一个静态成员一样,创建其对象无需依赖外部类对象(访问一个类的静态成员也无需依赖这个类的对象,因为它是独立于所有类的对象的)。但是于此同时,静态内部类中也无法访问外部类的非静态成员,因为外部类的非静态成员是属于每一个外部类对象的,而本身静态内部类就是独立外部类对象存在的,所以静态内部类不能访问外部类的非静态成员,而外部类依然可以访问静态内部类对象的所有访问权限的成员,这一点和普通内部类无异。
3.匿名内部类(重点)
本质
package com.hansp.InnerClass.anonymous;
public class Demo01 {
public static void main(String[] args) {
Outer04 outer04 = new Outer04();
outer04.method();
}
}
class Outer04{//外部类
private int n1=10;//属性
public void method(){//方法
//基于接口的匿名内部类
//1.需求使用接口A,传统写一个类实现接口,然后创建对象
//2.现在需求的只用一次方法,但是创建一个对象很不划算
//3.使用匿名内部类搞定,简化开发
//4.编译类型? A
//5.运行类型? 就是匿名内部类 XXXX就是Outer04$1(外部类名称+$1)
/*
我们看底层,是有下面这个操作的,会分配一个类名
class XXXX implements A{
public void cry(){
System.out.println("老虎嗷嗷叫...");
}
}
*/
//6.jdk顶层先创建匿名内部类Outer$1,就马上创建了其实例,并且把地址返回给A tiger
//7.匿名内部类只能,使用一次就不能使用了(只能new一次),但是这个对象还在
A tiger=new A(){
public void cry(){
System.out.println("老虎嗷嗷叫...");
}
};
tiger.cry();
/*
1.Father编译类型Father
2.Father运行类型匿名内部类Outer$2
3.在底层类似于
class Outer$2 extends Father{
@Override
public void test() {
System.out.println("匿名内部类重写了test方法");
}
}
*/
Father father=new Father("jack"){
};
father.test();
//还有一种基于抽象类的匿名内部类,必须实现抽象类里面的抽象方法
B b=new B() {
@Override
public void say() {
System.out.println("匿名内部类重写抽象方法");
}
};
b.say();
}
}
interface A{
public void cry();
}
class Father{
public Father(String name){//构造器
}
public void test(){//方法
}
}
abstract class B{
abstract public void say();
}
匿名内部类的使用
注意:匿名内部类使用大部分场景都是
继承抽象类,接口重写抽象方法,调用
但也可以继承普通类,调用原来(被继承的)有的方法
但是没什么意义
看底层,了解底层你就懂了!
总而言之,匿名内部类不依靠抽象方法存在
而是依靠继承/实现存在
创建类变量或者接口变量
用
B b=new B(){//B是接口或者是类
代码块里面可以重写(实现)B的方法(如果B是抽象类一定要重写里面的抽象方法,接口的话要实现里面的所有方法,普通类可以不写,不写的话就是调用原来的声明)
}
最后调用b.方法来进行匿名内部类使用 (当然要在作用域里(对应的方法或者代码块))
可以看上面的代码有具体使用
疏忽了,也可以这样使用
匿名内部类使用细节
匿名内部类既是一个类的定义也是一个对象的创建,因此从语法上看它既有定义类的特征也有创建对象的特征
匿名内部的使用细节
1.访问外部类所有成员(包括私有)
2.不能添加访问修饰符
3.作用域在定义它的代码块或者方法中
4.匿名内部类访问外部类直接访问即可
5.外部其他类不能访问匿名内部类
6.如果外部类和匿名内部类的成员重名的话,遵循就近原则,想访问外部类成员使用
(外部类名.this.成员名)去访问
说明一下:
匿名内部类也可以创建一些自己的方法,但是你是调用不了的,因为编译是过不去的,只有运行类型里面有这个方法,而且运行类型你还不知道类名,所以你肯定在这个对象里调用不到这个方法所以这样做,(这样做是没有意义的)。
当然你可以在重写方法里调用它自身的方法也是可以的。
匿名内部类的经典使用场景
package com.hansp.InnerClass;
public class Exercise {
public static void main(String[] args) {
f1(new A() {
@Override
public void show() {
System.out.println("知道如何应用匿名内部类了吗?");
}
});
}
//静态方法形参是接口类型
public static void f1(A a){
a.show();
}
}interface A{
void show();
}
传统的建类继承重写创建对象然后调用相比于这个麻烦,但是你如果要多次用就用传统的
如果你只想用一次,就可以用这种匿名内部类的形式
更加灵活
一道小题
package com.hansp.InnerClass;
public class exe02 {
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{
public void ring();
}
class CellPhone {
public void alarmClock(Bell bell){
bell.ring();
}
}
4.局部内部类
相当于一个局部变量的类
在外类的方法(代码块不常用)里定义的类(有类名)叫局部内部类
此中的Inner叫局部内部类
使用细节
1.局部内部类定义在外部类的局部位置,通常是方法中
2.局部内部类可以直接访问外部类的所有属性和方法(包括private)
3.局部内部类不能添加访问修饰符,但是可以用final修饰 (不可被继承)
4.局部内部类的作用域仅仅在定义它的方法或者代码块中(所以要调用里面的属性或者方法一定是在外部类的方法里建对象然后调用的!!!)
5.局部内部类可以直接访问外部类的成员
6.外部类访问局部内部类的成员,创建对象再访问 (必须在作用域内!!!看4)
运行
7.外部其他类不能访问局部内部类(因为局部内部类相当于一个局部变量)
8.如果外部类和局部内部类的成员重名遵守就近原则
想访问外部类成员用
外部类名.this.成员名调用(最后这个外部类名.this指向的是一个外部类对象,这个对象调用内部所在方法,比如new Outer02().m1();,指向的是new开辟出来的那块对象)