内部类
在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。广泛意义上的内部类一般来说包括这四种:成员内部类、局部内部类、匿名内部类和静态内部类。下面就先来了解一下这四种内部类的用法。
成员内部类
成员内部类就是在一个类的内部在定义一个类,这个内部户类就相当于是这个外部类的一个成员一样
成员内部类可以访问外部类的属性和方法包括private,而外部类要访问内部类的属性或者方法,则需要创个一个内部类对象,通过这个对象来访问内部类的成员
如果内部类和外部类有相同的成员变量,则会发生隐式转换,变量最终会变成内部类的变量,如果非要访问外部类的变量,则需要通过
外部类.this.成员变量
外部类.this.成员方法
public class Student {
private String name;
private int age;
public void getStudent() {
System.out.println("访问的是外部类, name = " + name + ", age = " + age);
new Book().getBook();
}
class Book {
private String name = "bookName";
public void getBook() {
System.out.println(
"访问的是内部类方法, Student.name = " + Student.this.name
+ ", name = " + name + ", age = " + age);
}
/**
* Getter method for property name.
*
* @return property value of name
*/
public String getName() {
return name;
}
/**
* Setter method for property name
*
* @param name value to be assigned to property name
*/
public void setName(String name) {
this.name = name;
}
}
/**
* Getter method for property name.
*
* @return property value of name
*/
public String getName() {
return name;
}
/**
* Setter method for property name
*
* @param name value to be assigned to property name
*/
public void setName(String name) {
this.name = name;
}
/**
* Getter method for property age.
*
* @return property value of age
*/
public int getAge() {
return age;
}
/**
* Setter method for property age
*
* @param age value to be assigned to property age
*/
public void setAge(int age) {
this.age = age;
}
public static void main(String[] args) {
Student stu = new Student();
stu.setAge(10);
stu.setName("student");
stu.getStudent();
}
}
结果:
访问的是外部类, name = student, age = 10
访问的是内部类方法, Student.name = student, name = bookName, age = 10
Process finished with exit code 0
成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。创建成员内部类对象的一般方式如下
public class Student {
private String name;
private int age;
private Book book;
public void getStudent() {
System.out.println("访问的是外部类, name = " + name + ", age = " + age);
new Book().getBook();
}
public Book getInstance() {
if (book == null) {
book = new Book();
}
return book;
}
class Book {
private String name = "bookName";
public void getBook() {
System.out.println(
"访问的是内部类方法, Student.name = " + Student.this.name
+ ", name = " + name + ", age = " + age);
}
/**
* Getter method for property name.
*
* @return property value of name
*/
public String getName() {
return name;
}
/**
* Setter method for property name
*
* @param name value to be assigned to property name
*/
public void setName(String name) {
this.name = name;
}
}
/**
* Getter method for property name.
*
* @return property value of name
*/
public String getName() {
return name;
}
/**
* Setter method for property name
*
* @param name value to be assigned to property name
*/
public void setName(String name) {
this.name = name;
}
/**
* Getter method for property age.
*
* @return property value of age
*/
public int getAge() {
return age;
}
/**
* Setter method for property age
*
* @param age value to be assigned to property age
*/
public void setAge(int age) {
this.age = age;
}
public static void main(String[] args) {
Student stu = new Student();
Student.Book book = stu.new Book();
//或者
book = stu.getInstance();
}
}
成员内部类相当于外部类的一个成员属性,也会有访问权限的控制,如果内部类是private,则只能在外部类的内部访问;如果是protected则可以在通过一包内访问;如果是public则都可以访问。
默认是protected,只能在同一个包下访问。
局部内部类
局部内部类就是在一个方法体内或者一个代码块内部定义一个类。
局部内部类相当于一个方法内部的局部变量,没有private、public的感念,只能在方法体内被访问。
public class Student {
private String name;
private int age;
public void getStudent() {
System.out.println("访问的是外部类, name = " + name + ", age = " + age);
class Book {
private String name;
public void getBook() {
System.out.println(
"访问的是内部类方法");
}
}
}
/**
* Getter method for property name.
*
* @return property value of name
*/
public String getName() {
return name;
}
/**
* Setter method for property name
*
* @param name value to be assigned to property name
*/
public void setName(String name) {
this.name = name;
}
/**
* Getter method for property age.
*
* @return property value of age
*/
public int getAge() {
return age;
}
/**
* Setter method for property age
*
* @param age value to be assigned to property age
*/
public void setAge(int age) {
this.age = age;
}
}
匿名内部类
匿名内部类应该是平时我们编写代码时用得最多的
一般使用匿名内部类的方法来编写事件监听代码。
同样的,匿名内部类也是不能有访问修饰符和static修饰符的。
匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。
匿名内部类在编译的时候由系统自动起名为Outter$1.class。一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。
/** 这段代码是从网上拷贝的,通常匿名内部类在实现回调接口、监听接口功能中使用最为广泛 */
scan_bt.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
});
history_bt.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
});
静态内部类
静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法,这点很好理解,因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象。
public class Test {
public static void main(String[] args) {
Outter.Inner inner = new Outter.Inner();
}
}
class Outter {
public Outter() {
}
static class Inner {
public Inner() {
}
}
}
其它知识点
内部类如何访问的外部类的成员
我们在编译一个还有内部类的外部类时,最终会编译出两个文件
外部类$内部类.class 和 外部类.class
对第一个class进行反编译,我们会发现原因所在
内部类会有一个指向外部类引用的指针,通过这个外部类的引用可以访问外部类的成员
即使我们定义的内部类的构造器是无参的,编译器也会给构造器添加一个参数,这个参数就是外部类的一个引用参数,最终在创建内部类对象时候会把一个外部类的引用赋值给内部类,这样内部类就可以通过这个引用访问到外部类,从这个方面可以看到,内部类是要依赖外部类才能存在的。
局部内部类和匿名内部类为什么只能访问final变量
我们首先看下如下这段代码
public void getStudent(final int a) {
final int b = 1;
new Thread(){
@Override
public void run() {
System.out.println(a);
System.out.println(b);
}
}.start();
}
首先上面的两个变量a/b,如果不加final则编译不过
我们先观察一下上面的代码,当方法执行结束后,局部变量就会立刻消失,而这个时候线程的生命周期极有可能还没有结束,这个时候如果线程要访问变量的值那该怎么办呢?
当编译器发现匿名内部类要依赖局部变量b时,首先会将这个 变量值拷贝一份到匿名内部类的常量池中,这样如果内部类要使用这些变量时,直接就能去常量池拿到值。
如果这个变量是一个表达式或者在编译期间无法确认值怎么办,比如上面例子中的a变量?
我们对匿名内部类反编译会发现这个,这个时候匿名内部类会在构造方法中增加一个参数,其中一个参数是指向外部类的引用参数,另一个参数就是上面的变量a的参数,即以参数的形势将a的值拷贝到匿名内部类中。
也就说如果局部变量的值在编译期间就可以确定,则直接在匿名内部里面创建一个拷贝。如果局部变量的值无法在编译期间确定,则通过构造器传参的方式来对拷贝进行初始化赋值。
以上只是说明了变量值和参数是怎么被匿名内部类使用的,还是没有说明final的问题,我们接着上面的说,a是以参数的形势传进来的,那么当我们在匿名内部类中对a进行修改操作后,会发生什么呢? 没错值不一致,这就是问题所在,因为外部方法调用结束后就退出了,留下了内部类自己在执行,这个时候如果内部类对传进来的变量进行了修改,那就会导致内外的值不一致,因此需要对变量值进行final修饰,确保值不被修改。
- 静态内部类有特殊的地方吗?
静态内部类是不依赖于外部类的,也就说可以在不创建外部类对象的情况下创建内部类的对象。另外,静态内部类是不持有指向外部类对象的引用的,通过反编译可以发现这个问题。
用途
1.每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整,
2.方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。
最后补充一点知识:关于成员内部类的继承问题。一般来说,内部类是很少用来作为继承用的。但是当用来继承的话,要注意两点:
1)成员内部类的引用方式必须为 Outter.Inner.
2)构造器中必须有指向外部类对象的引用,并通过这个引用调用super()
public class Student {
class Book{
}
}
class Test extends Student.Book{
//必须有外部类的引用参数
public Test(Student stu){
//必须有这句
stu.super();
}
}
如果构造方法中缺少上面标注的两点,则会直接报错