Java基础内部类学习笔记
一.什么是内部类?
一个类的内部嵌套了另一个类,这个类叫内部类,是类的第五大成员(属性,方法,构造器,代码块,内部类)。
内部类最大的特点就是可以直接访问私有属性,它本质上还是一个类。
基本语法:
public class Localinner {
//外部其他类
public static void main(String[] args) {
}
}
class Outer{
//外部类
class Inner{
//内部类
}
}
class Other{
//外部其他类
}
二.内部类的分类
内部类可具体分为四种。
定义在外部类局部位置上(比如方法或者代码块内):
1. 局部内部类(有类名)
局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。
它有以下几个特点:
- 可以直接访问外部类的所有成员,包含私有的
- 不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用 修饰符的。但是可以使用final 修饰,因为局部变量也可以使用final
- 作用域:仅仅在定义它的方法或代码块中。
- 外部类访问局部内部类的成员必须先创建内部类对象,再访问(注意:在作用域内才能创建对象)
- 外部其他类不能访问局部内部类(因为局部内部类地位是一个局部变量)
- 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,可以使用(外部类名.this.成员)去访问
- 同一作用域的内部类可以继承另一个内部类(本质上还是一个类)
示例代码:
public class LocalInnerClass {
public static void main(String[] args) {
Outer02 outer02 = new Outer02();
outer02.m1();
}
}
class Outer02{
//外部类
private int n1 = 100; //外部类私有属性
private void m2(){
//外部类私有方法
System.out.println("外部类私有方法");
};
public void m1(){
//外部类的m1()方法
class Inner02{
//内部类(这个类不能添加访问修饰符,但是可以用final修饰(不想让同一个方法内的其他内部类继承))
private int n1=200;
public void f1(){
System.out.println("外部类私有属性"+"n1="+Outer02.this.n1); //访问外部类属性 (哪个对象调用了m1()方法,Outer02.this指的就是哪个对象,比如在这里它指的就是Outer02这个对象本身)
System.out.println("内部类私有属性"+"n1="+this.n1); //访问内部类属性
m2(); //访问外部类方法
}
}
class Inner03 extends Inner02{
//另一个内部类继承
}
Inner03 inner03 = new Inner03(); //外部类在方法中创建这个内部类
inner03.f1();
}
}
输出结果:
2. 匿名内部类(没有类名,重点!)
案例引入:
需求:想使用A接口,并创建对象
- 传统方式,是写一个类,实现该接口,并创建对象。如下所示
public class Test {
public static void main(String[] args) {
A outerClass = new Tiger();
outerClass.cry();
}
}
interface A {
//接口
public void cry();
}
class Tiger implements A{
@Override
public void cry() {
System.out.println("老虎叫....");
}
}
- 但是如果想要Tiger/Dog类只是使用一次,后面再不使用,就可以使用匿名内部类来简化开发。
匿名内部类是定义在外部类的局部位置,比如方法中或者代码块中,并且没有类名,对象实例化出来后该内部类就消失了(但是对象还是可以反复使用)。
基本语法:
new类或接口(参数列表){
类或接口里的东西
};
基于接口的匿名内部类
示例代码如下。
public class AnonymousInnerClass {
//外部其他类
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
outerClass.method();
}
}
class OuterClass{
//外部类
private int n1=10;
public void method(){
//编译类型是A,运行类型就是这个匿名内部类
A tiger = new A(){
//使用new接口(){}的方式创建匿名内部类
@Override
public void cry() {
System.out.println("老虎叫..叫..");
}
};
System.out.println("tiger的运行类型="+tiger.getClass()); //获取tiger对象的运行类型
tiger.cry();
}
}
interface A {
//接口
public void cry();
}
接口是不能被直接new出来的,所以在jdk的底层实现实际上是经过了如下转换。(jdk底层在创建匿名内部类OuterClass$1,立即马上就创建了0uter04$1实例,并且把地址返回给tiger)
class XXXX implements A{
//这个xxxx就是这个匿名内部类的名字,但是是由系统自动去分配的(分配规则是-->外部类名$1 之后每个匿名内部类的编号都会以第一个外部类名为主自增)
@Override
public void cry() {
System.out.println("老虎叫..叫..");
}
}
基于类的匿名内部类
和基于接口的方式相似,
示例代码如下。
public class AnonymousInnerClass {
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
outerClass.method();
}
}
class OuterClass{
public void method(){
//基于类的匿名内部类
Father a = new Father("cjy"){
//注意这里是有带{ }的,所以是一个匿名内部类,而且这里的cjy会传给Father的构造器
{
System.out.println("这是一个匿名内部类");
}
};
}
}
class Father {
public Father(String name){
}
public void test(){
}
}
实际上在jdk的底层实现实际上是经过了如下转换的。
class xxxxx2 extends Father {
}
还有一点要注意的是如果是基于抽象类创建匿名内部类的话里面的抽象方法必须重写。
练习使用:
要求如下。
- 有一个铃声接口Bell,里面有个ring方法。
- 有一个手机类Cellphone,具有闹钟功能alarmclock,参数是Bell类型
- 测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了
- 再传入另一个匿名内部类(对象),打印:小伙伴上课了
public class AnonymousInnerClassDemo2 {
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){
bell.ring();
}
}
运行结果:
总结:匿名内部类也可以理解为另一种局部内部类(特点和局部内部类相似)只不过是没有名字而且只能实例一次,也通常被用于临时需要重写一些类或接口的方法,简化开发(某些类只想用一次,使用匿名内部类会自己去实现接口或继承类,我们只需要重写方法)。
定义在外部类的成员位置上:
3.成员内部类(没用static修饰)
成员内部类是定义在外部类的成员位置(跟属性,方法,代码块,构造器同级),并且没有static修饰。
成员内部类有下面几个特点:
- 可以直接访问外部类的所有成员,包含私有的
- 可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
- 作用域和外部类的其他成员一样,为整个类体
- 外部类访问内部类需要在在外部类的成员方法或代码块中创建成员内部类对象,再访问
- 外部其他类拿到成员内部类对象有两种方式(下边代码示例)
- 如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)访问
代码示例:
public class MemberInnerClass {
public static void main(String[] args) {
Outerclass outerClass = new Outerclass();
outerClass.t1();
//外部其他类拿到成员内部类对象的两种方式
//第一种方式
Outerclass.Innerclass innerclass1 = outerClass.new Innerclass();
//第二种方式
Outerclass.Innerclass innerclass2 = outerClass.t2();
}
}
class Outerclass{
//外部类
private int n1 = 10;
public String name = "张三";
public void m1(){
//外部类的私有方法
}
class Innerclass{
//成员内部类
private int n1 = 20;
public void say(){
System.out.println("n1 = "+ Outerclass.this.n1); //使用外部类私有属性
System.out.println("n1 = "+ n1); //使用内部类私有属性
m1(); //调用外部类私有方法
}
}
public void t1(){
///创建成员内部类的对象,然后使用相关的方法
Innerclass innerclass = new Innerclass();
innerclass.say();
}
//返回一个成员内部类对象
public Innerclass t2(){
///创建成员内部类的对象,然后使用相关的方法
return new Innerclass();
}
}
4.成员静态内部类(使用static修饰)
静态内部类是定义在外部类的成员位置,并且有static修饰。
成员静态内部类有下面几个特点。
- 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
- 可以添加任意访问修饰符(public.protected、默认、private),因为它的地位就是一个成员。
- 作用域:同其他的成员,为整个类体
- 被static修饰的内部类可以直接作为一个普通类来使用,而不需实例一个外部类
- 外部其他类拿到静态成员内部类对象有两种方式(下边代码示例)
- 如果外部类和静态内部类的成员重名时,静态内部类访问的时默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.成员)访问
示例代码:
public class StaticMemberInnerClass {
public static void main(String[] args) {
//创建外部类
Outer outer = new Outer();
outer.m1();
//外部其他类访问静态内部类对象(两种方式)
//第一种方式 (直接创建)
Outer.Inner inner1 = new Outer.Inner(); //直接使用不需要new(前提是满足访问权限)
inner1.say();
//第二种方式 (通过方法的返回值获得对象)
Outer.Inner inner2 = outer.m3();
inner2.say();
}
}
class Outer{
//外部类
private int n1 = 10;
private static String name = "张三";
public static class Inner{
//静态成员内部类
private static String name = "张四";
public void say(){
System.out.println(Outer.name); //只能访问静态属性(访问外部类属性)
System.out.println(name); //只能访问静态属性(访问内部类属性)
}
}
//成员方法创建内部类
public void m1(){
Inner inner = new Inner();
inner.say();
}
//返回一个静态成员内部类对象
public Inner m3(){
return new Inner();
}
}