内部类
内部类,即定义在一个类的内部的类。为什么有内部类呢?
我们知道,在java中类是单继承的,一个类只能继承另一个具体类或抽象类(可以实现多个接口)。这种设计的目的是因为在多继承中,当多个父类中有重复的属性或者方法时,子类的调用结果会含糊不清,因此用了单继承。
而使用内部类的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
在我们程序设计中有时候会存在一些使用接口很难解决的问题,这个时候我们可以利用内部类提供的、可以继承多个具体的或者抽象的类的能力来解决这些程序设计问题。可以这样说,接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整。
成员内部类也是最普通的内部类,它是外围类的一个成员,所以它可以无限制的访问外围类的所有成员属性和方法,尽管是private的,但是外围类要访问内部类的成员属性和方法则需要通过内部类实例来访问。
在成员内部类中要注意两点:
静态内部类与非静态内部类之间存在一个最大的区别:非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,但是静态内部类却没有。
没有这个引用就意味着:
局部内部类是嵌套在方法和作用于内的,对于这个类的使用主要是应用与解决比较复杂的问题,
想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的
所以就产生了局部内部类,局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法和属性中被使用,出了该方法和属性就会失效。
1、匿名内部类是没有访问修饰符的。
2、new 匿名内部类,这个类首先是要存在的。
3、当所在方法的形参需要被匿名内部类使用,那么这个形参就必须为final。
4、匿名内部类没有明面上的构造方法,编译器会自动生成一个引用外部类的构造方法。
匿名对象
匿名对象:没有名字的对象
匿名对象的特征: 语法上: 只创建对象,但是不用变量来接收
匿名对象的使用: 1.匿名对象也是一个对象,具有对象的所有功能 2.每一次使用匿名对象时,都是一个新的对象, 每次创建匿名对象都是不同的对象,一个匿名对象,只能使用一次,即匿名对象只能调用一次
3.匿名对象只在堆内存中开辟空间,而不存在栈内存的引用
class Dog{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Override
protected void onPause() {
clock.stop();
super.onPause();
Dog dog=new Dog();
dog.getName();
}
如果要执行的任务需要一个对象,但却不值得创建全新的对象(原因可能是所需的类过于简单,或者是由于它只在一个方法内部使用),匿名类就显得非常有用
如我们需要一个监听类,并且在实例化这个监听类的时候才需要实现这个监听类内部的方法,那么使用匿名类就最方便了
1、内部类可以用多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立。 2、内部类并没有令人迷惑的“is-a”关系,他就是一个独立的实体。 3、内部类提供了更好的封装,除了该外围类,其他类都不能访问。 4、创建内部类对象的时刻并不依赖于外围类对象的创建。
具体来说,内部类信息(属性、方法)可以和外部类重名;内部类是具有类的基本特征的独立实体;可以利用访问修饰符隐藏内部类的实施细节,提供了更好的封装;静态内部类使用时可直接使用,不需先创造外部类。
一、内部类: (1)内部类的同名方法 内部类可以调用外部类的方法,如果内部类有同名方法必须使用"OuterClass.this.MethodName()"格式调用(其中OuterClass与MethodName换成实际外部类名及其方法;this为关键字,表示对外部类的引用);若内部类无同名方法可以直接调用外部类的方法。 但外围类无法直接调用内部类的private方法,外部类同样无法直接调用其它类的private方法。
注意:内部类直接使用外部类的方法与该方法的权限与是否static无关,它取决于内部类是否有同名方法。
public class OuterClass { private void outerMethod() { System.out.println("It's Method of OuterClass"); } public static void main(String[] args) { OuterClass t = new OuterClass(); OuterClass.Innerclass in = t.new Innerclass(); in.innerMethod(); } class Innerclass { public void innerMethod() { OuterClass.this.outerMethod();// 内部类成员方法与外部类成员方法同名时,使用this调用外部类的方法 outerMethod();// 内部类没有同名方法时执行外部类的方法 } private void outerMethod() { System.out.println("It's Method of Innerclass"); } } } 输出结果为: It's Method of OuterClass It's Method of Innerclass (2)内部类访问外部类的变量必须声明为final 方法中的局部变量,方法结束后这个变量就要释放掉,final保证这个变量始终指向一个对象。 首先,内部类和外部类其实是处于同一个级别,内部类不会因为定义在方法中就会随着方法的执行完毕而跟随者被销毁。问题就来了,如果外部类的方法中的变量不定义final,那么当外部类方法执行完毕的时候,这个局部变量肯定也就被GC了,然而内部类的某个方法还没有执行完,这个时候他所引用的外部变量已经找不到了。如果定义为final,java会将这个变量复制一份作为成员变量内置于内部类中,这样的话,由于final所修饰的值始终无法改变,所以这个变量所指向的内存区域就不会变。 注意,若使用JDK1.8,方法中内部类的方法是可以直接访问外部类的方法的局部变量,并且不需要声明为final类型。 public class OuterClass { int num1 = 0;// 成员变量 private void outerMethod() { int num2 = 0;// 方法内的局部变量 class Innerclass_1 { public void innerMethod() { System.out.println(num1);// 方法中内部类的方法,可以正常访问外部类的成员变量 System.out.println(num2);// JDK1.8以前,方法中内部类的方法,不能直接访问外部类的方法的局部变量,必须声明为final } } } } 如果使用JDK1.8以前的版本,Eclipse会出现如下错误提示:
(3)内部类的实例化 内部类实例化不同于普通类,普通类可以在任意需要的时候实例化,而内部类必须在外层类实例化以后方可实例化,并与外部类建立关系 因此在外部类中的非static方法中,是可以实例化内部类对象 private void outerMethod() { System.out.println("It's Method of OuterClass"); Innerclass in = new Innerclass();//在外部类的outerMethod方法中实例化内部类是可以啊 } 但在static方法中,就要注意啦!!!!不能在static方法中直接new内部类,否则出现错误: No enclosing instance of type OuterClass is accessible. Must qualify the allocation with an enclosing instance of type OuterClass (e.g. x.new A() where x is an instance of OuterClass). 这是因为静态方法是在类实例化之前就可以使用的,通过类名调用,这时动态内部类都还没实例化呢 如果想在Static方法中new内部类,可以把内部类声明为Static public class OuterClass { private void outerMethod() { System.out.println("It's Method of OuterClass"); } public static void main(String[] args) { Innerclass in = new Innerclass(); in.innerMethod(); } static class Innerclass {//把内部类声明为static public void innerMethod() { System.out.println("It's Method of innerMethod"); } } } 当然,一般不使用static的方式,而是推荐这种方法:x.new A() ,其中 x是外部类OuterClass的实例,A是内部类Innerclass
public class OuterClass { private void outerMethod() { System.out.println("It's Method of OuterClass"); } public static void main(String[] args) { OuterClass.Innerclass in = new OuterClass().new Innerclass();//使用x.new A()的方式 in.innerMethod(); } class Innerclass { public void innerMethod() { System.out.println("It's Method of innerMethod"); } } } x.new A() ,其中 x是外部类OuterClass的实例,A是类部类Innerclass,当然可以拆分如下,这样就显然很明白啦: public static void main(String[] args) { OuterClass out = new OuterClass();//外部实例 OuterClass.Innerclass in = out.new Innerclass();//外部实例.new 外部类 in.innerMethod(); }
(4)什么情况下使用内部类
典型的情况是,内部类继承自某个类或实现某个接口,内部类的代码操作创建其的外层类的对象。所以你可以认为内部类提供了某种进入其外层类的窗口。 使用内部类最吸引人的原因是:每个内部类都能独立地继承自一个(接口的)实现,所以无论外层类是否已经继承了某个(接口的)实现,对于内部类都没有影响。如果没有内部类提供的可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。从这个角度看,内部类使得多重继承的解决方案变得完整。接口解决了部分问题,而内部类有效地实现了“多重继承”。 (5)在静态方法中实例化内部类例子:(内部类放在静态方法中) package javatest2; public class JavaTest2 { public static void main(String[] args) { class Boy implements Person { public void say() {// 匿名内部类自定义的方法say System.out.println("say方法调用"); } @Override public void speak() {// 实现接口的的方法speak System.out.println("speak方法调用"); } } Person per = new Boy(); per.speak();// 可调用 per.say();// 不能调用 } } interface Person { public void speak(); } per.speak()可调用,而per.say()不能调用,这时因为per是Person对象,要想调用子类的方法,可以强制向下转型为:((Boy) per).say();或者直接改为Boy per = new Boy();。从中可发现,要想调用内部类的自定义的方法,必须通过内部类的对象来调用。那么,匿名内部类连名字都没有,怎么调用内部类自定义的方法? (二)匿名内部类 匿名内部类也就是没有名字的内部类正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写,但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口,但最多只能继承一个父类,或实现一个接口。 关于匿名内部类还有如下两条规则: 1)匿名内部类不能是抽象类,因为系统在创建匿名内部类的时候,会立即创建内部类的对象。因此不允许将匿名内部类定义成抽象类。 2)匿名内部类不等定义构造器(构造方法),因为匿名内部类没有类名,所以无法定义构造器,但匿名内部类可以定义实例初始化块, 怎样判断一个匿名类的存在啊?看不见名字,感觉只是父类new出一个对象而已,没有匿名类的名字。 先看段伪代码 abstract class Father(){ .... } public class Test{ Father f1 = new Father(){ .... } //这里就是有个匿名内部类 } 一般来说,new 一个对象时小括号后应该是分号,也就是new出对象该语句就结束了。但是出现匿名内部类就不一样,小括号后跟的是大括号,大括号中是该new 出对象的具体的实现方法。因为我们知道,一个抽象类是不能直接new 的,必须先有实现类了我们才能new出它的实现类。上面的伪代码就是表示new 的是Father的实现类,这个实现类是个匿名内部类。 其实拆分上面的匿名内部类可为: class SonOne extends Father{ ... //这里的代码和上面匿名内部类,大括号中的代码是一样的 } public class Test{ Father f1 = new SonOne() ; } 先看一个例子,体会一下匿名内部类的用法:
运行结果:eat something 可以看到,我们直接将抽象类Person中的方法在大括号中实现了,这样便可以省略一个类的书写。并且,匿名内部类还能用于接口上 public class JavaTest2 { public static void main(String[] args) { Person per = new Person() { public void say() {// 匿名内部类自定义的方法say System.out.println("say方法调用"); } @Override public void speak() {// 实现接口的的方法speak System.out.println("speak方法调用"); } }; per.speak();// 可调用 per.say();// 出错,不能调用 } } interface Person { public void speak(); } 这里per.speak()是可以正常调用的,但per.say()不能调用,为什么呢?注意Person per = new Person()创建的是Person的对象,而非匿名内部类的对象。其实匿名内部类连名字都没有,你咋实例对象去调用它的方法呢?但继承父类的方法和实现的方法是可以正常调用的,本例子中,匿名内部类实现了接口Person的speak方法,因此可以借助Person的对象去调用。 若你确实想调用匿名内部类的自定义的方法say(),当然也有方法: (1)类似于speak方法的使用,先在Person接口中声明say()方法,再在匿名内部类中覆写此方法。 (2)其实匿名内部类中隐含一个匿名对象,通过该方法可以直接调用say()和speak()方法;代码修改如下: public class JavaTest2 { public static void main(String[] args) { new Person() { public void say() {// 匿名内部类自定义的方法say System.out.println("say方法调用"); } @Override public void speak() {// 实现接口的的方法speak System.out.println("speak方法调用"); } }.say();// 直接调用匿名内部类的方法 } } interface Person { public void speak(); }