内部类的定义
可以将一个类的定义放在另一个类的定义内部,这就是内部类。
内部类的修饰符
对于一个外部类来说,内部类就像是外部类的孩子,外部类是永远可以看到内部类,不管内部类的修饰符是啥,那么,内部类的修饰对什么有作用,就是决定其他类能否看到外部类的内部类,如果内部类是private修饰,那么其他是看不到的。
内部类的构造器
在java的四种内部类中,除了匿名内部类没有构造方法,其他的都可以自己定义哦
创建内部类的对象
public class Outer {
public static void main(String[] args) {
Outer t = new Outer();
inner i = t.new inner();
}
/*
* 内部类的修饰4种都可以
*/
public class inner{
}
}
创建某个内部类对象,必须要利用外部类的对象通过.new来创建内部类:
OuterClass.InnerClass innerClass = outerClass.new InnerClass();
java 内部类的类别
在Java中内部类主要分为成员内部类、局部内部类、匿名内部类、静态内部类。
成员内部类
作为外部类的一个成员存在的类。
成员内部类和外部类的关系
当我们在创建一个成员内部类的时候,它无形中就与外围类有了一种联系,依赖于这种联系,它可以无限制地访问外围类的元素。
public class Outer {
public static void main(String[] args) {
Outer t = new Outer();
inner i = t.new inner();
i.show();
i.getOuter().show1();
}
/*
* 内部类的修饰4种都可以
*/
public class inner{
public void show() {
System.out.println("我是内部类的方法");
}
public Outer getOuter() {
return Outer.this;//获得一个外部对象的引用
}
}
public void show1() {
System.out.println("我是外部类的方法");
System.out.println(Outer.this.a);//也可以通过这个方法直接取访问外部类的变量和方法。非静态内部类能够访问外部类成员是因为它持有外部类对象的引用 Outer.this, 就像子类对像能够访问父类成员是持有父类对象引用super一样
}
}
我们通过在内部类中,提供了一个方法,可以去获取外部类的对象,有了这个对象,你还不是想干嘛就干嘛呀。
注意:**该内部类的修饰符可以是private、protected、public以及默认的访问权限 。**
public class Outer {
public static void main(String[] args) {
Outer t = new Outer();
t.getInner();
}
public class inner{
}
public inner getInner() {
return new inner();
}
}
注意点:
- 成员内部类中,不能包含static修饰的变量和方法,如果一个非static的内部类如果具有static的属性或者方法,那么就会出现一种情况:内部类未加载,但是却试图在内存中创建static的属性和方法,这当然是错误的。原因:类还不存在,但却希望操作它的属性和方法。
- 对于成员内部类,它是依赖外部类,所以建议大家,在获取成员内部类,可以提供一个getXxx方法,去获取。
局部内部类
定义在方法或者某个作用域内(比如if代码块)内部的内部类,主要用来创建一个类来辅助我们的解决方案,它只要出了方法、作用域,就没有用。
局部内部类的类名前面不可以加任何修饰符。
public B1 getMessage(String message) {
class inner extends B1{
public inner(String s) {
}
}
return new inner(message);
}
没有实际的业务逻辑,就举个例子。
局部内部类,只是用来帮助我们解决问题,出了方法,就没有用,也没有办法去创建它。
匿名内部类
创建内部类没有名字
public class Outer {
public static void main(String[] args) {
Outer t = new Outer();
}
public B show(int a,int b) {
return new B() {//创建一个匿名内部类实现抽象类B
@Override
int add() {
// TODO Auto-generated method stub
return a+b;
}
};
}
public add show1(int a,int b) {
return new add() {//创建一个匿名内部类实现接口add
@Override
public int add() {
// TODO Auto-generated method stub
return a+b;
}
};
}
}
interface add{
int add();
}
abstract class B{
abstract int add();
}
- 匿名内部类只能继承一个类和一个接口
- 对于匿名内部类的使用它是存在一个缺陷的,就是它仅能被使用一次,创建匿名内部类时它会立即创建一个该类的实例,该类的定义会立即消失,所以匿名内部类是不能够被重复使用。
- 匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。(最容易忘的,不能定义static属性、方法)
- 匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。
- 匿名内部类由于没有名字,所以没有构造函数。
在局部内部类和匿名内部中为什么变量要加final修饰
两种情况:
- 当所在的方法的形参需要被内部类里面使用时,该形参必须为final。
- 当方法的局部变量需要被内部类使用,也必须是final。
public static void main(String[] args) {
Test275 t = new Test275();
t.validateNumber("12345--45609-", "123-----99900");
}
String regexExpression="[^0-9]";
public void validateNumber(String number1,String number2) {
int numberlength = 10;
class PhoneNumber{
String formattedPhoneNumber = null;
public PhoneNumber(String phonenumber) {
super();
// TODO Auto-generated constructor stub
String currentnumber = phonenumber.replaceAll(regexExpression, "");
System.out.println(currentnumber);
if(currentnumber.length()==numberlength) {
formattedPhoneNumber = currentnumber;
}else {
formattedPhoneNumber =null;
}
}
public String getFormattedPhoneNumber() {
return formattedPhoneNumber;
}
public void getOrignNumber() {
System.out.println(number1+"\t"+number2);
}
}
PhoneNumber p = new PhoneNumber(number1);
p.getOrignNumber();
System.out.println( p.getFormattedPhoneNumber());
PhoneNumber p1 = new PhoneNumber(number2);
System.out.println( p1.getFormattedPhoneNumber());
}
在内部类中访问numberlength、number1、number2均会报
Cannot refer to the non-final local variable numberlength defined in an enclosing
需要你在这些参数、变量前面加final修饰,这是为什么呢。
经过参考网上的例子,我发现,局部内部类或者是匿名内部类内部类调用方法的形参或者方法的属性,是通过内部类的构造器去传,实际上构造器是一种浅拷贝,基本数值类型是拷贝一个副本,而对于引用类型,则是拷贝的引用,值还是指向原先的值。
因为两者从外表看起来是同一个东西,实际上却不是这样,如果内部类改掉了这些参数的值也不可能影响到原参数,然而这样却失去了参数的一致性,因为从编程人员的角度来看他们是同一个东西,如果编程人员在程序设计的时候在内部类中改掉参数的值,但是外部调用的时候又发现值其实没有被改掉,这就让人非常的难以理解和接受,为了避免这种尴尬的问题存在,所以编译器设计人员把内部类能够使用的参数设定为必须是final来规避这种莫名其妙错误的存在。”final 修饰基本类型,是该值不可变,final 修饰引用,引用指向不可变,不允许它改变引用指向(这里有一点注意一下:final修饰的引用变量是引用指向不可变,变量的值是可以变,和这里不矛盾,指向的都是同一个值,还是满足我呀)这样就保证参数一致性啦。
上面说了这么多,你了解意思就好,因为在jdk 1.8之后,这些就不要烦了,jdk不强求加final了。
匿名内部类的初始化
我们都知道构造函数最重要的一个作用就是初始化个属性,在之前介绍过,匿名内部类是没有构造函数的。所以它的初始化工作就要借助于构造代码块去完成。
public Inner getInner(String name, int age) {
return new Inner(){
String name1;
int age1;
{
//name = "sdasd";这样是不可以,因为jdk 1.8之后,虽然不要求你
//显示的加上final,实际上底层是帮你做了这件事,所以执行这句肯定
//编译不可以通过,你只有权利访问,无权修改
name1 = name;
age1 =age;
}
};
}
静态内部类
用static修饰的内部类,我们称之为静态内部类,静态内部类与非静态内部类之间存在一个最大的区别,我们知道非静态内部类(成员内部类)在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,有了这个引用,你想调什么方法,就用什么方法,想调啥字段,就是啥字段。但是静态内部类却没有。没有这个引用就意味,静态内部类只能访问外部类的静态属性和方法啦。
静态内部类创建是不需要依赖于外围类的。(所以我们更习惯称之它为静态嵌套类)
public class Outer {
private int a =6;
private static int b =9;
public static void main(String[] args) {
System.out.println(inner.c);
}
public void show() {
System.out.println(Outer.inner.c);
//静态内部类可以等同看外部类做静态属性
//这种方式只可以访问静态内部类的静态属性和静态方法
System.out.println(inner.c);//这种其实和上面的创建静态内部类的对象很类似,只是省去该类,inner这个静态就在Outer种,可以默认省去,但是你在其他类中,就需要添加外部类的类名哦
//这种方式只可以访问静态内部类的静态属性
System.out.println(new inner().d);//构造静态内部类的对象,那你想玩什么都行。
}
/*
* 四种修饰符都可以
*/
public static class inner{
private static int c=7;
private int d = 6;
public void show() {
System.out.println(Outer.b);
//静态内部类可以访问外部类的静态属性和方法,非静态的无法访问,这种方式,直接通过外部类的名字可以直接访问外部类的静态属性和方法,可以通过间接的去访问非静态属性,直接创建外部类的对象呀,想访问啥都可以。
}
}
}
内部类实现java多继承
我们知道在java中,是只支持单继承,之前可以实现接口的多实现来间接实现多继承,今天我们可以通过内部类来间接实现多继承。
public class Test318 {
public String getName() {
return "laoqiang";
}
}
public class Test319 {
public int getAge() {
return 12;
}
}
public class Test320 {
class A extends Test318{
@Override
public String getName() {
// TODO Auto-generated method stub
return super.getName();
}
}
class B extends Test319{
@Override
public int getAge() {
// TODO Auto-generated method stub
return super.getAge();
}
}
public static void main(String[] args) {
new Test320().new A().getName();
new Test320().new B().getAge();
}
}
通过内部类,我们在Test320中,伪继承使用了两个类的getName、getAge方法,间接实现多继承
内部类加载时机
我们都知道内部类相当于外部类的儿子,但是内部类是延迟加载的,只有第一次调用之后,才会加载。