内部类(可以使用内部类来实现多继承)
- 定义:在一个类中进行其他机构的嵌套
内部类的简单定义:
class Outer{
private String msg="Hello world!";
//定义一个内部类
class Inner{
//定义一个普通方法
public void print(){
//调用msg属性
System.out.println(msg);
}
}
//在外部类中定义一个方法,该方法负责产生内部类对象并且调用print()方法
public void fun(){
//内部类对象
Inner in=new Inner();
// 内部类提供的print()方法
in.print();
}
}
public class Test{
public static void main(String[] args) {
//外部类对象
Outer out=new Outer();
//外部类方法
out.fun();
}
}
// 打印结果如下
//Hello word!
将内部类拆分到外部
class Outer{
private String msg = "Hello World!" ;
//通过此方法才能取得msg属性
public String getMsg(){
return this.msg ;
}
//3.现在由out对象调用了fun()方法
public void fun(){
//4.this表示当前对象
Inner in = new Inner(this);
//7.调用方法
in.print();
}
}
class Inner{
private Outer out;
//5.Inner.out = mian.out
public Inner(Outer out){
//6.引用传递
this.out = out ;
}
//8.执行此方法
public void print(){
System.out.println(out.getMsg());
}
}
public class Test{
public static void main(String[] args) {
//1. 实例化Outter类对象
Outer out = new Outer();
//2.调用Outter类方法
out.fun();
}
}
内部类的优点:
-
内部类与外部类可以互相访问彼此的私有域(最大优点)
-
内部类可以实现Java的单继承局限
使用内部类实现多继承
class A{
private String name="A的私有类";
public String getName(){
return name;
}
}
class B{
private int age=20;
public int getAge(){
return age;
}
}
class Outter{
private class InnerClassA extends A{
public String name(){
return super.getName();
}
}
private class InnerClassB extends B{
public int age(){
return super.getAge();
}
}
public String name(){
return new InnerClassA().name();
}
public int age(){
return new InnerClassB().age();
}
}
public class Test{
public static void main(String[] args) {
Outter outter=new Outter();
System.out.println(outter.name());
System.out.println(outter.age());
}
}
-
内部类可以对同一包中其他类隐藏起来,仅供外部类使用
内部类的缺点:结构复杂
内部类与外部类的关系
-
对于非静态内部类而言,内部类的创建需要依靠外部类的实例化对象,在没有外部类对象之前是无法创建内部类的
-
内部类是一个相对独立的个体,与外部类不是is - a 关系
-
内部类可以直接访问外部类的元素(包含私有域),但是外部类不可以直接访问内部类元素,需要通过内部类的引用间接访问。
内部类直接访问外部元素
class Outter{
private String outName;
private int outAge;
class Inner {
private int InnerAge;
public Inner() {
Outter.this.outName="I am Outter class";
Outter.this.outAge=20;
}
public void display(){
System.out.println(outName);
System.out.println(outAge);
}
}
}
public class Test{
public static void main(String[] args) {
Outter.Inner inner=new Outter().new Inner();
inner.display();
}
}
外部类通过内部类间接引用访问内部类元素
class Outter{
public void display(){
//外部类访问内部元素,需要通过内部类引用访问
Inner inner=new Inner();
inner.display();
}
class Inner {
public void display(){
System.out.println("I am InnerClass");
}
}
}
public class Test{
public static void main(String[] args) {
Outter out=new Outter();
out.display();
}
}
创建内部类的语法(在外部类的外部创建内部类) *** 灰常重要 ***
创建非静态内部类:
-
外部类 . 内部类 内部类引用 = new 外部类(). new 内部类();
-
Outter . Inner in = new Outter( ) . new Inner( ) ; 创建内部类的对象
隐藏的对象 Outter . this 表示外部类对象
创建静态内部类
-
外部类 . 内部类 内部类引用 = new 外部类 . 内部类();
-
Outter . Inner in = new Outter . Inner(); //直接产生内部类,没有外部类产生
内部类的分类 ***重要***
-
成员内部类 --- 成员方法
1 . 成员内部类不能存在任何static变量或方法,可以访问外部类的静态域,不用拥有静态属性
2 . 内部类可以使用private封装,表示私有内部类,该内部类仅供外部类使用(HashMap中Entry内部类,ArreyList的Node内部类) ***重要***
3 . 成员内部类必须依附外部类new Outter(). new Inner();
-
静态内部类 --- 静态方法
1 . 静态内部类的创建不需要外部类,可以直接创建
2 . 静态内部类不可以访问外部类的任何非static域。
使用static创建内部类
class Outer{
private static String msg="Hello World!";
//定义一个内部类
static class Inner {
//此时只能使用外部类中的static操作
public void print(){
//调用msg属性
System.out.println(msg);
}
}
//在外部类中定义一个方法,该方法负责产生内部类对象并且调用print()方法
public void fun(){
//内部类对象
Inner in=new Inner();
//内部类提供的print()方法
in.print();
}
}
public class Test{
public static void main(String[] args) {
Outer.Inner in=new Outer.Inner();
in.print();
}
}
-
方法内部类
1.方法内部类不能使用任何访问权限修饰符 public private protected 均不允许
2.方法内部类对外(方法外)
完全隐藏,除了该方法可以访问它之外,其余地方均不能访问
3.方法内部类要想使用方法形参,该方法必须使用final声明(JDK8变为隐式final声明)
4.只能访问 ***
-
匿名内部类 --- 方法内部类的特殊版本 - lambda表达式的第一步
1.匿名内部类必须继承一个抽象类或者实现一个接口
2.匿名内部类没有构造方法,因为它没有类名,没有访问修饰符
3.匿名内部类中不能存在任何静态成员或方法
4.与局部内部相同匿名内部类也可以引用方法形参。此形参也必须声明为 final
//缺少方法主体,或声明抽象
//可以使用 void test();这种方法,
//而这样的方法只能出现在接口或抽象类中
class MyInterface{
void test(){
}
}
class Outer{
private int num;
public void display(int para){
new MyInterface(){
public void test(){
System.out.println("匿名内部类");
}
}.test();
}
}
public class Test{
public static void main(String[] args){
Outer outer=new Outer();
outer.display(29);
}
}
三、使用的形参为何要为final
转载自--https://blog.csdn.net/chenssy/article/details/13170015
我们给匿名内部类传递参数的时候,若该形参在内部类中需要被使用,那么该形参必须要为final。也就是说:当所在的方法的形参需要被内部类里面使用时,该形参必须为final。
为什么必须要为final呢?
首先我们知道在内部类编译成功后,它会产生一个class文件,该class文件与外部类并不是同一class文件,仅仅只保留对外部类的引用。当外部类传入的参数需要被内部类调用时,从java程序的角度来看是直接被调用:
public class OuterClass {
public void display(final String name,String age){
class InnerClass{
void display(){
System.out.println(name);
}
}
}
}
从上面代码中看好像name参数应该是被内部类直接调用?其实不然,在java编译之后实际的操作如下:
public class OuterClass$InnerClass {
public InnerClass(String name,String age){
this.InnerClass$name = name;
this.InnerClass$age = age;
}
public void display(){
System.out.println(this.InnerClass$name + "----" + this.InnerClass$age );
}
}
所以从上面代码来看,内部类并不是直接调用方法传递的参数,而是利用自身的构造器对传入的参数进行备份,自己内部方法调用的实际上时自己的属性而不是外部方法传递进来的参数。
直到这里还没有解释为什么是final?在内部类中的属性和外部方法的参数两者从外表上看是同一个东西,但实际上却不是,所以他们两者是可以任意变化的,也就是说在内部类中我对属性的改变并不会影响到外部的形参,而然这从程序员的角度来看这是不可行的,毕竟站在程序的角度来看这两个根本就是同一个,如果内部类该变了,而外部方法的形参却没有改变这是难以理解和不可接受的,所以为了保持参数的一致性,就规定使用final来避免形参的不改变。
简单理解就是,拷贝引用,为了避免引用值发生改变,例如被外部类的方法修改等,而导致内部类得到的值不一致,于是用final来让该引用不可改变。
故如果定义了一个匿名内部类,并且希望它使用一个其外部定义的参数,那么编译器会要求该参数引用是final的。