内部类详解
最近一周学习了内部类的知识,以前写android的时候总是看到内部类,那个时候其实也不知道这是内部类,没有好好理解,看书学到的东西还是比较全面的。
我想分为几篇文章记录一下我学的东西
1.内部类的分类以及用法
2.内部类的好处
内部类的分类以及用法
内部类的分类**
1.普通内部类
2.在方法和作用域中的内部类
3.匿名内部类
4.嵌套类
1.普通内部类
内部类的创建方式挺简单,就是把一个类放在另一个类里面,如下
public class Fruit {
public String name = "fruit";
public class Apple {
public String name="apple";
}
}
这样就可以了,创建的时候和普通类一样,可以使用public,private,protected和默认的,访问规则如下
(1)对外部类的外部来说它和普通类的访问权限一样的,但创建方式不同,需采用.new的方法,如下:
class Fruit {
public String name = "fruit";
public class Apple {
public String name="apple";
}
}
public class Main{
public static void main(String[] args){
Fruit f = new Fruit();
Fruit.Apple a = f.new Apple(); // 这里
System.out.println(f.name + " " + a.name);
}
}
结果
fruit apple
必须要先创建外部类的对象,然后用这个对象来创建这个内部类,对象需要被声明为 外部类.内部类的格式,创建的时候使用 外部类对象.new的方式
当然也可以在外部类创建一个方法,这个方法返回一个内部类的对象,像这样
class Fruit {
public String name = "fruit";
public class Apple {
public String name="apple";
}
public Apple getApple(){ // 这里
return new Apple();
}
}
调用getApple() 就可以得到这个类了
(2)对外部类来说,外部类可以访问内部的所有东西,不管声明成什么样
测试代码如下:
class Fruit {
public String name = "fruit";
private class Apple { // 声明为private
private String name="apple"; // 声明为private
}
public void change(){ // 更改
Apple apple = new Apple();
apple.name = "aa";
System.out.println(apple.name);
}
}
public class Main{
public static void main(String[] args){
Fruit f = new Fruit();
f.change();
}
}
结果
aa
(3)内部类可以访问外部类的所有东西
测试代码是这样:
class Fruit {
private String name = "fruit";
private static int count = 0;
private class Apple {
private void changeFather(){ // 更改父类对象的name和父类的count
Fruit.this.name = "Fruit"; // Fruit.this
Fruit.count++;
}
}
public void change(){
Apple apple = new Apple();
apple.changeFather();
System.out.println("father.name="+this.name);
System.out.println("Father.count="+count);
}
}
public class Main{
public static void main(String[] args){
Fruit f = new Fruit();
f.change();
}
}
结果
father.name=Fruit
Father.count=1
Apple可以更改外部类对象的name属性以及外部类的count,有一点需要注意下内部类访问外部类的属性和方法时可以使用 外部类.this的方式,即Fruit.this
其实内部类中包含了一个外部类对象的引用,所以必须先创建一个外部类的对象才能去创建内部类对象
java程序编译的时候会编译出两个.class,一个是Fruit.class,一个是Fruit$Apple.class,所以创建内部类时要声明为 Fruit.Apple这样才能找到
内部类感觉起来就像是外部类的一个成员变量,只是它自己又包含了几个成员变量,所以外部类可以自由的访问内部类的全部,内部类可以访问外部类的全部
2.在方法和作用域中的内部类
顾名思义就是这个内部类被放在外部类的一个方法中或者一个作用域中,作用域就像是这样
if(ture){
//作用域中
}
如果将内部类声明在方法中,那么只有这个方法中才可以访问
如果将它声明在作用域中,那么只有这个作用域中才可以访问
为了方便,直接就把它声明在一个方法的作用域中了
class Fruit {
public void change(){
int a = 1;
if(a > 0) {
class Apple{ // Apple类
private String name = "apple";
}
System.out.println(new Apple().name);
}
}
}
public class Main{
public static void main(String[] args){
Fruit f = new Fruit();
f.change();
}
}
结果
apple
将Apple类的声明放在 if(a>0){}
的作用域中,只有这个作用域才可以访问,需要注意的是,class前面不能加修饰符。如果其他文件中的类想要使用这个内部类,虽然可以直接返回它,但是函数的返回值没法写,因为内部类是在作用域中,如下反应了这个问题
public XXX getInner(){
class Inner{
}
return Inner;
}
就是XXX根本不知道写什么,而实际上这个问题正是内部类的精髓之一,解决的方式就是让内部类实现一个接口或者继承抽象类/普通类
如下:
interface HaveName{ // 声明一个接口
String getName();
}
class Fruit {
public HaveName getHaveName(){
class FruitHaveName implements HaveName{ //实现该接口
public String getName(){
return "Fruit";
}
}
return new FruitHaveName(); // 返回内部类的一个对象
}
}
public class Main{
public static void main(String[] args){
Fruit f = new Fruit();
HaveName hn = f.getHaveName();
System.out.println(hn.getName());
}
}
这是内部类的一个典型的用法,感觉和集合的iterator很像
3.匿名内部类
匿名内部类就是不需要有类名的内部类,这个还是有点常见的
interface HaveName{
String getName();
}
class Fruit {
public HaveName getHaveName(){
return new HaveName(){ // 匿名内部类
public String getName(){
return "Fruit";
}
};
}
}
public class Main{
public static void main(String[] args){
Fruit f = new Fruit();
HaveName hn = f.getHaveName();
System.out.println(hn.getName());
}
}
创建一个匿名内部类不需要名字,所以它不能有构造器,格式需要是new 基类名() { 基类实现 }
的这种方式,和普通实现了接口的类一样,方法需要声明为public的。与方法中的内部类相比,这种方式不需要起名字,更简单,但是如果需要有构造器的话,就需要使用方法中的内部类了,因为它有名字。
有一个问题,如果内部类的基类是一个抽象类,这个抽象类有一个带参数的构造器,那该如何创建匿名内部类。基类名(xxx)
,采用此方式就可以创建了,可以自动的将此xxx参数传递给基类的构造器。
还有一点需要注意如果内部类中修改了方法传递过来的参数,这个参数是不可修改的,也就是说方法参数需要时final的,不知道为啥,我这个编译器不需要声明称final的也行,只是修改参数的时候报错
public HaveName getHaveName(String name){
return new HaveName(){
public String getName(){
//name = "aaaaa"; //这个编译不能通过
return name;
}
};
}
虽然看起来内部类是外部类的一部分,但实际上内部类被编译成一个单独的类,从编译结果Outer.Inner.class就可看的出来,想一下如果其他的类能随意的修改另一个类中的参数,结果可能是不可预料的,所以java不允许在内部类中修改(这里不太懂)
4.嵌套类
嵌套类就是将内部类用static修饰,就像是普通的静态成员那样,创建的时候不许要先创建外部类的对象,像静态函数一样,不能访问外部类的非静态的方法
例子如下:
class Fruit {
public static class Name{
public String name = "Fruit";
}
}
public class Main{
public static void main(String[] args){
Fruit.Name fn = new Fruit.Name();
System.out.println(fn.name);
}
}
嵌套类中是不包含外部类对象的引用的,所以不需要先创建外部类对象,再由外部类对象创建它
如果有什么错误,请务必告诉我