在Android开发的过程中遇到一个内存泄漏问题,问题产生的原因涉及Java内部类和Android的消息机制,
所以首先再复习一遍内部类的知识并且记录下来。
- 在一个类内部定义的类可以称为内部类。
- 内部类可以和外部类进行通信
10.1 创建内部类
创建内部类的形式就是把类的定义放在外围类的里面:
/*************************************************************************
> File Name: Parcell.java
> Created Time: 2018年07月05日 星期四 14时07分52秒
************************************************************************/
//: innerclasses/Paarcell.class
// Create=ing Inner classes
public class Parcell{
class Contents{
private int i = 11;
public int value(){
return i;
}
}
class Destination{
private String label;
Destination(String whereTO){
label = whereTO;
}
public String readLabel(){
return label;
}
}
// Using inner classes looks just like
// using any other class,within Parcell
public void ship(String dest){
Contents c = new Contents();
Destination d = new Destination(dest);
System.out.println(d.readLabel());
}
public static void main(String[] args){
Parcell p = new Parcell();
p.ship("Tasmania");
}
}
如果想在外部类的非静态方法之外的任意位置创建某个内部类的对象,必须具体指明这个对象的类型: OuterClassName.InnerClassName
10.2 链接到外部类
内部类除了可以隐藏和组织代码, 它还有其他的用途。
当生成一个内部类对象时,此对象和制造它的外围类对象(enclosing object)之间就有了一种联系,所以它能访问其外围对象的所有成员,而不需要任何特殊条件。此外,内部类还拥有其外围类的所有元素的访问权。举例说明:
/*************************************************************************
> File Name: Sequence.java
> Created Time: 2018年07月05日 星期四 14时34分00秒
************************************************************************/
//:innerclasses/Sequence.java
//Holds a sequence of objects
interface Selector{
boolean end();
Object current();
void next();
}
public class Sequence{
private Object[] items;
private int next = 0;
public Sequence(int size){
items = new Object[size];
}
public void add(Object x){
if (next < items.length){
items[next++] = x;
}
}
private class SequenceSelector implements Selector{
private int i = 0 ;
public boolean end(){return i == items.length;}
public Object current(){
return items[i];
}
public void next(){
if (i < items.length){
i++;
}
}
}
public Selector selector(){
return new SequenceSelector();
}
public static void main(String[] args){
Sequence sequence = new Sequence(10);
for (int i = 0 ; i < 10 ; i ++){
sequence.add(Integer.toString(i));
}
Selector selector = sequence.selector();
while(!selector.end()){
System.out.println(selector.current() + " ");
selector.next();
}
}
}
上面的例子内部类自动拥有对其外部类所有成员的访问权限。
一个可能造成内存泄漏的原因
某个外部类对象创建了一个内部类对象的时候,此时内部类对象必定会秘密捕获一个指向外部类对象的引用。在当你访问外部类的成员的时候,是这个秘密捕获的引用来帮你选择外部类的成员
如果内部类不是static
的静态内部类,那么这样的内部类对象只能在与其外部类相关联的情况下被创建。构建内部类对象的时,需要指定一个外部类对象的引用,如果访问不到这个外部类对象的引用,那么编译器则会报错。
10.3 使用.this 和 .new
- 在内部类中如果你需要对外部类对象的引用,使用外部类的名字+‘.this’ ==
OutClassName.this
。这样产生的引用自动的具有正确的类型,它是在编译期时被知晓和检查,没有任何运行时开销。 - 在外围类内部创建一个内部类和不同类的创建方法一致。在其他类里创建一个内部类,则必须在new表达式中提供外部类对象的引用,这需要使用
OutClassObj.new InnerClass
。因为在拥有外部类对象之前是不能创建内部类对象的,除非你的内部类是嵌套类,也就是静态内部类。
10.4 内部类和向上转型
向上转型为基类或者接口的时候,与普通类一致,得到的是指向基类或者接口的引用,能够方便地隐藏实现的细节。
尤其是,非Public内部类,通过这种方式可以完全阻止任何依赖于类型的编码,并且完全隐藏了实现的细节。
10.5 在方法和作用域内的内部类
相对于前四节平凡的内部类用法,内部类的语法覆盖了大量的其他更加难以理解的技术。
在一个方法里面或者在任意的作用域内定义内部类。这么做的理由有二:
1. 你实现了某个类型的接口,于是创建并返回对其的引用。临时丰富接口的引用,接口的匿名内部类。。哈哈哈
2. 你要解决一个复杂问题,向创建一个类来辅助你的解决方案,但是又不希望这个类是公共可用的。
其他几种内部类:
* 一个定义在方法中的类
* 一个定义在作用域内部的类,此作用域在方法的内部。
* 一个实现了接口的匿名类
* 一个匿名类,它扩展了有非默认构造器的类
* 一个匿名类,它执行字段初始化
* 一个匿名类,它通过实例初始化实现构造
10.6 匿名内部类
平常编码中遇到较多的一种。
首先是一个奇怪的例子:
/*************************************************************************
> File Name: Parcel7.java
> Author: ma6174
> Mail: ma6174@163.com
> Created Time: 2018年07月05日 星期四 16时31分43秒
************************************************************************/
//:innerclasses/Parcel7.java
//Returning an instance of an anonymous inner class
interface Contents{
void printValue();
}
public class Parcel7{
public Contents contents(){
return new Contents(){
private int i = 11;
public int value(){
return i;
}
};//Semicolon required in this case
}
public static void main(String[] args){
Parcel7 p = new Parcel7();
Contents = p.contents();
}
}
如果基类构造器是一个有参构造器,那么只要将合适的参数传递给构造器即可。
常遇 如果在匿名内部类里面使用一个在其外部定义的对象,那么编译器会要求这个参数引用的是final的。如果传参给其基类的构造器则不需要final修饰,因为它不会在匿名内部类中直接被使用。
10.6.1 再访工厂方法
/*************************************************************************
> File Name: Factories.java
> Created Time: 2018年07月05日 星期四 17时22分29秒
************************************************************************/
interface Service{
void method1();
void method2();
}
interface ServiceFactory{
Service getService();
}
class Implementation1 implements Service{
private Implementation1(){}
public void method1(){
System.out.println("Implementation1 method1");
}
public void method2(){
System.out.println("Implementation1 method2");
}
public static ServiceFactory factory = new ServiceFactory(){
public Service getService(){
return new Implementation1();
}
};
}
class Implementation2 implements Service{
private Implementation2(){}
public void method1(){
System.out.println("Implementation2 method1");
}
public void method2(){
System.out.println("Implementation2 method2");
}
public static ServiceFactory factory = new ServiceFactory(){
public Service getService(){
return new Implementation2();
}
};
}
public class Factories{
public static void serviceConsumer(ServiceFactory fact){
Service s = fact.getService();
s.method1();
s.method2();
}
public static void main(String[] args){
serviceConsumer(Implementation1.factory);
serviceConsumer(Implementation2.factory);
}
}
在普通工厂方法中,添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类。上述将具体的工厂类放在了匿名函数当中作为产品类的static域。
10.7 嵌套类 静态内部类罗
如果不需要内部类对象和外围类对象之间有联系,可以将内部类声明为静态static。
嵌套类意味着:
1. 要创建嵌套类的对象,并不需要其外围类的对象
2. 不能从嵌套类的对象中访问非静态的外围类对象
举例:
/*************************************************************************
> File Name: Parcel11.java
> Created Time: 2018年07月05日 星期四 17时49分27秒
************************************************************************/
//:innerclasses/Parcell11.java
//Nested classes (static inner classes)
interface Contents{
int value();
}
interface Destination{
String readLanel();
}
public class Parcel11{
private static class ParcelContents implements Contents{
private int i = 11;
public int value(){return i;}
}
protected static class ParcelDestination implements Destination{
private String label;
private ParcelDestination(String whereTo){
label = whereTo;
}
public String readLanel(){
return label;
}
public static void f(){}
static int x = 10;
static class AnotherLevel{
public static void f(){}
static int x = 10;
}
}
public static Destination destination(String s){
return new ParcelDestination(s);
}
public static Contents contents(){
return new ParcelContents();
}
public static void main(String[] args){
Contents c = contents();
Destination d = destination("Tasimania");
}
}
10.7.1 接口内部的类
例子:
/*************************************************************************
> File Name: ClassInInterface.java
> Created Time: 2018年07月05日 星期四 18时07分38秒
************************************************************************/
public interface ClassInInterface{
void helloWorld();
class Test implements ClassInInterface{
public void helloWorld(){
System.out.println("Hello,World!");
}
public static void main(String[] args){
new Test().helloWorld();
}
}
}