雪习新知识:Java 内部类

嵌套类,内部类,静态内部类,静态嵌套类。匿名类,成员类,局部类,傻傻分不清?

各种类,各种累!本文为你抽丝剥茧,庖丁解牛。娓娓道来。

首先声明一下,本文要讲的不是一个文件中面并列的两个类,而是在一个类里面定义另外一个类。

1. 几个样例

例1:Adapter

public class ListViewActivity extends Activity {

    // some stuff

    class MyAdapter extends BaseAdapter {
        // some stuff
    }
}

例2:AlertDialog.Builder

使用方法:

public class ListViewActivity extends Activity {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("对话框");
        AlertDialog dialog = builder.create();
        dialog.show();
}

AlertDialog.Builder 相应的 SDK 源码:

public class AlertDialog extends Dialog implements DialogInterface {

    // unrelated code

    public static class Builder {
        private final AlertController.AlertParams P;
        private int mTheme;
        ...
    }

    // unrelated code
}

例3:Thread

new Thread() { 

     @Override
     public void run() {
         // TODO Auto-generated method stub
     }
}.start(); 

例4:Button.OnClickListener

((Button)findViewById(R.id.start)).setOnClickListener(new Button.OnClickListener() {
    @Override
    public void onClick(View v) {
    }
});

当中,OnClickListener 是 interface,相应的源码在 View.java 中:

public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {

    // more code

    public interface OnClickListener {
        void onClick(View v);
    }

    // more code
}

例5:

public class Parcel4 {
    public Destination destination(String s) {
        class PDestination implements Destination {
            private String label; 

            private PDestination(String whereTo) {
                label = whereTo;
            } 

            public String readLabel() {
                return label;
            }
        }
        return new PDestination(s);
    } 

    public static void main(String[] args) {
        Parcel4 p = new Parcel4();
        Destination d = p.destination("Tasmania");
    }
} 

public class Parcel5 {
    private void internalTracking(boolean b) {
        if (b) {
            class TrackingSlip {
                private String id;
                TrackingSlip(String s) {
                    id = s;
                }
                String getSlip() {
                    return id;
                }
            }
            TrackingSlip ts = new TrackingSlip("slip");
            String s = ts.getSlip();
        }
    } 

    public void track() {
        internalTracking(true);
    } 

    public static void main(String[] args) {
        Parcel5 p = new Parcel5();
        p.track();
    }
} 

例6:Android Guide 对于 Fragment 的官方文档 Example (注意 TitlesFragment 是 static 的):

public static class TitlesFragment extends ListFragment {
    boolean mDualPane;
    int mCurCheckPosition = 0;

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
    ...
    }
}

public static class DetailsActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (getResources().getConfiguration().orientation
                == Configuration.ORIENTATION_LANDSCAPE) {
            // If the screen is now in landscape mode, we can show the
            // dialog in-line with the list so we don‘t need this activity.
            finish();
            return;
        }

        if (savedInstanceState == null) {
            // During initial setup, plug in the details fragment.
            DetailsFragment details = new DetailsFragment();
            details.setArguments(getIntent().getExtras());
            getFragmentManager().beginTransaction().add(android.R.id.content, details).commit();
        }
    }
}

例7:Handler

 mTvRef;

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (mTvRef.get() instanceof TextView) {
                mTvRef.get().setText("test");
            }
        }

        MyHandler(TextView textView) {
            mTvRef = new WeakReference(textView);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }" data-snippet-id="ext.b3b567912b1cd1ee7662e7ac3008a2a5" data-snippet-saved="false" data-codota-status="done">// Handler声明为static类,对外部类成员的引用改由WeakReference实现,如:
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tipTv = (TextView) findViewById(R.id.tiptv);

        mHandler = new MyHandler(tipTv);
        mHandler.sendEmptyMessageDelayed(MSG_TICK, 2000);
    }

    static class MyHandler extends Handler {
        private final WeakReference<TextView> mTvRef;

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (mTvRef.get() instanceof TextView) {
                mTvRef.get().setText("test");
            }
        }

        MyHandler(TextView textView) {
            mTvRef = new WeakReference<TextView>(textView);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

例8:Google 给的 listview 优化的建议:

static class ViewHolder {
    TextView text;
    ImageView icon;
}

public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;

        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.list_item_icon_text, null);

            holder = new ViewHolder();
            holder.text = (TextView) convertView.findViewById(R.id.text);
            holder.icon = (ImageView) convertView.findViewById(R.id.icon);

            convertView.setTag(holder);
        } else {
        }
        holder.text.setText(DATA[position]);
        holder.icon.setImageBitmap((position & 1) == 1 ?

mIcon1 : mIcon2);

        return convertView;
    }

2. 眼花缭乱的各种类

那么问题来了,以上样例中。哪几个属于内部类?

要推断某个类是不是内部类。就要知道内部类的定义。

那么问题来了,谁下的内部类的定义才是最权威的?

《Thinking in Java》。《Effective Java》,《Java 核心技术》《xxx天精通Java》。《Java 高手进阶》。还是某某博客?

都不是。

Java 技术领域有 3 本书:

  • Java Virtual Machine Specification
  • Java Language Specification
  • Design Patterns: Elements of Reusable Object-Oriented Software

从底层的虚拟机原理。到包罗万象的编程语言规范,再到顶层的系统设计,应有尽有,比人类历史上销量第一的《圣经》和第二的《毛主席语录》都权威。

Java Language Specification(jls8,8.1.3节)对内部类的定义:

An inner class is a nested class that is not explicitly or implicitly declared static.

翻译成中文是:内部类是没有显式或隐式的 static 修饰的嵌套类(nested class)。

将上述定义切割成 3 个重点:

  • explicitly or implicitly

    看到 explicitly or implicitly 这个词组,不知道大家有没有想到 Android 中 Activity 的跳转方式?Activity 的跳转方式有显式(explicit)和隐式(implicit) 2 种方式;在这里。explicitly 是指带 static 修饰符,implicitly 是指尽管没带 static 可是等效于带 static 的情况,实际是指在类内部声明接口的情况。

  • static

    请看第3节

  • nested class

    请看第 3 节

3. 嵌套类(nested class)

一张图看懂各种类

Java 同意在一个类里面定义另外一个类,里面的这个类被称为嵌套类。

形式例如以下:

class OuterClass {
    ...
    class NestedClass {
        ...
    }
}

当中。依据依据嵌套类前面有无 static 修饰符,能够将嵌套类分为静态嵌套类(static nested class)和非静态嵌套类(non-static nested class)。后者又称为内部类(inner class)。

class OuterClass {
    ...
    static class StaticNestedClass {
        ...
    }
    class InnerClass {
        ...
    }
}

3.1 内部类(inner class)

3.1.1 局部内部类(local class)

局部内部类是指定义在语句块内的类,语句块是指成对大括号形成的块,能够是一个方法体。或者 if 语句,或者 for 循环语句。

以下这个样例(引自 Orcale 官方文档 并做了简单改动),作用是验证电话号码位数是否合法。在 validatePhoneNumber 方法体内定义了局部内部类 PhoneNumber:

public class LocalClassExample {

    static String regularExpression = "[^0-9]";

    public static void validatePhoneNumber(
        String phoneNumber1, String phoneNumber2) {

        final int numberLength = 11;

        // Valid in JDK 8 and later:

        // int numberLength = 11;

        class PhoneNumber {

            String formattedPhoneNumber = null;

            PhoneNumber(String phoneNumber){
                // numberLength = 7;
                String currentNumber = phoneNumber.replaceAll(
                  regularExpression, "");
                if (currentNumber.length() == numberLength)
                    formattedPhoneNumber = currentNumber;
                else
                    formattedPhoneNumber = null;
            }

            public String getNumber() {
                return formattedPhoneNumber;
            }

            // Valid in JDK 8 and later:

//            public void printOriginalNumbers() {
//                System.out.println("Original numbers are " + phoneNumber1 +
//                    " and " + phoneNumber2);
//            }
        }

        PhoneNumber myNumber1 = new PhoneNumber(phoneNumber1);
        PhoneNumber myNumber2 = new PhoneNumber(phoneNumber2);

        // Valid in JDK 8 and later:

//        myNumber1.printOriginalNumbers();

        if (myNumber1.getNumber() == null)
            System.out.println("First number is invalid");
        else
            System.out.println("First number is " + myNumber1.getNumber());
        if (myNumber2.getNumber() == null)
            System.out.println("Second number is invalid");
        else
            System.out.println("Second number is " + myNumber2.getNumber());

    }

    public static void main(String... args) {
        validatePhoneNumber("123-456-78901", "456-7890");
    }
}

上述样例首先过滤掉全部非 0-9 数字的字符。然后检查剩下的数字是否够 11 位,输出例如以下:

First number is 12345678901
Second number is invalid
Accessing Members of an Enclosing Class

和内部类一样,局部内部类不能定义或声明 static 类型的成员方法或成员变量(常量除外)。定义在 static 方法内部的类。如 PhoneNumber。仅仅能引用外部类的 static 成员。

比方,假设不把 regularExpression 声明为 static,Java 编译器会抛出相似“non-static variable regularExpression cannot be referenced from a static context.”的异常信息。

局部内部类不能是 static 的。能够引用语句块的实例成员,并且不能包括 static 类型的成员。

在一个语句块中不能声明 interface。由于 interface 天生就是 static 的。

比如,以下的代码是无法编译的由于 interface HelloThere 在 greetInEnglish 方法体内:

public void greetInEnglish() {
        interface HelloThere {
           public void greet();
        }
        class EnglishHelloThere implements HelloThere {
            public void greet() {
                System.out.println("Hello " + name);
            }
        }
        HelloThere myGreeting = new EnglishHelloThere();
        myGreeting.greet();
    }

局部内部类中不同意声明 static 类型的成员方法或成员 interface。以下的代码段无法编译由于方法 EnglishGoodbye.sayGoodbye 是 static 的,编译器会报错:“modifier ‘static’ is only allowed in constant variable declaration”:

    public void sayGoodbyeInEnglish() {
        class EnglishGoodbye {
            public static void sayGoodbye() {
                System.out.println("Bye bye");
            }
        }
        EnglishGoodbye.sayGoodbye();
    }

局部内部类能够拥有的 static 类型的成员是常量(常量是基本类型的数据结构,或者是字符串,被 final 修饰。并且能在编译期间通过常量表达式赋初值,一般是字符串或者算术表达式)。下列代码段能够编译通过由于 EnglishGoodbye.farewell 是常量:

    public void sayGoodbyeInEnglish() {
        class EnglishGoodbye {
            public static final String farewell = "Bye bye";
            public void sayGoodbye() {
                System.out.println(farewell);
            }
        }
        EnglishGoodbye myEnglishGoodbye = new EnglishGoodbye();
        myEnglishGoodbye.sayGoodbye();
    }

3.1.2 成员内部类(member class)

3.2 静态嵌套类(static nested class)

3.2.0 interface

首先来说下嵌套的 interface,interface 属于 class 的一种,之所以把它放在此节,是由于 interface 是一种隐式的静态嵌套类。思考例如以下两点:

  • View.OnClickListener,OnClickListener 是定义在 View.java 内部的 interface,我们的使用方法是 class Clazz implements View.OnClickListener,这可不就是 static nested class 的使用方法吗?!
  • 全部的 interface。无论是嵌套在某个类内部的 interface。还是单独定义在某个文件中名称与文件名称相同的 interface,是能够声明成员变量的,并且变量是 static final 即常量类型(注意这是到 IT 笔试/面试题)。这也佐证 interface 是 static 的。

    至于我们为什么非常少在 interface 中声明成员变量,那是由于 interface 的初衷是赋予子类某些“行为”即子类实现 interface 的方法,完毕自己独特的“行为”,通过超类(父类或 interface)指针指向子类对象的形式,在执行过程中自己去调用子类的独特的方法,这就是“多态”,这就是“面向接口编程”。

3.2.1 实例化方式

与内部类的实例化相反,静态嵌套类的实例化不依赖其外部类的实例,即不须要先实例化外部类,然后才干由外部类实例创建静态嵌套类实例。

并且。静态嵌套类仅仅能訪问其外部类的静态成员。

/* 以下程序演示怎样在java中创建静态嵌套类和内部类 */
class OuterClass {
   private static String msg = "GeeksForGeeks";

   // 静态嵌套类
   public static class StaticNestedClass{

       // 静态嵌套类仅仅能訪问外部类的静态成员
       public void printMessage() {

         // 试着将msg改成非静态的,这将导致编译错误
         System.out.println("Message from nested static class: " + msg);
       }
    }

    // 内部类
    public class InnerClass {

       // 无论是静态方法还是非静态方法都能够在内部类中訪问
       public void display() {
          System.out.println("Message from non-static nested class: "+ msg);
       }
    }
} 

class Main {
    // 怎么创建静态嵌套类和内部类的实例
    public static void main(String args[]){

       // 创建静态嵌套类的实例
       OuterClass.StaticNestedClass printer = new OuterClass.StaticNestedClass();

       // 创建静态嵌套类的非静态方法
       printer.printMessage();   

       // 为了创建内部类,我们须要外部类的实例
       OuterClass outer = new OuterClass();
       OuterClass.InnerClass inner = outer.new InnerClass();

       // 调用内部类的非静态方法
       inner.display();

       // 我们也能够结合以上步骤,一步创建的内部类实例
       OuterClass.InnerClass innerObject = new OuterClass().new InnerClass();

       // 相同我们如今能够调用内部类方法
       innerObject.display();
    }
}

3.2.2 使用场景

静态嵌套类的使用场景,能够參考其语法,即,静态嵌套类不依赖外部类的详细实例。外部类的全部实例公用的部分。

以计算器类 Caculator 为例。Caculator 中的运算符 Operator 我们打算使用嵌套类来实现。

Operator 的成员变量 PLUS、MINUS 等变量。显然是全部 Caculator 公用的,这时我们最好将其声明为 static。这样全部的运算符都能够通过 Caculator.Operator.PLUS 的方式调用。

此外。能够使用静态嵌套类实现单例模式(很多其它实现方式见《单例模式》)。

实例代码例如以下:

public class Singleton {
    private Singleton() {}
    private static class SingletonHolder {
        public static Singleton INSTANCE = new Singleton();
    }

    public Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

3.2.3 优先使用静态嵌套类

内部类实例会持有其外部实例的引用,这样会增大内部类实例占用的空间。

并且。GC 依据可达性原则回收对象。内部实例引用外部实例会添加外部实例存活的时间,不利于外部实例被及时回收,不利于性能提升。

比較稳妥的方法是,全部的内部类全部声明成 static 的,然后依据 IDE 的提示进行改动。实在无法进行下去的(如内部类引用了外部类的某个非静态成员,而该静态成员无法改成 static 类型),则使用内部类,否则就使用静态嵌套类。

在使用 Firebug 进行代码检查时,Firebug 会提示将内部类改为静态嵌套类,并将该问题归为性能大类,截图例如以下:

给出的理由例如以下:

This class is an inner class, but does not use its embedded reference to the object which created it. This reference makes the instances of the class larger, and may keep the reference to the creator object alive longer than necessary. If possible, the class should be made static.

关于 Fragment 是否应该声明为 static,stackoverflow 上有过一篇帖子,可供參考:What is the design logic behind Fragments as static inner classes vs standalone public classes?

时间: 2024-12-09 11:33:46

雪习新知识:Java 内部类的相关文章

[转] Java内部类详解

作者:海子 出处:http://www.cnblogs.com/dolphin0520/ 本博客中未标明转载的文章归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利. Java内部类详解 说起内部类这个词,想必很多人都不陌生,但是又会觉得不熟悉.原因是平时编写代码时可能用到的场景不多,用得最多的是在有事件监听的情况下,并且即使用到也很少去总结内部类的用法.今天我们就来一探究竟.下面是本文的目录大纲: 一.内部类基础 二.

Java内部类详解 2

Java内部类详解 说起内部类这个词,想必很多人都不陌生,但是又会觉得不熟悉.原因是平时编写代码时可能用到的场景不多,用得最多的是在有事件监听的情况下,并且即使用到也很少去总结内部类的用法.今天我们就来一探究竟.下面是本文的目录大纲: 一.内部类基础 二.深入理解内部类 三.内部类的使用场景和好处 四.常见的与内部类相关的笔试面试题 若有不正之处,请多谅解并欢迎批评指正. 请尊重作者劳动成果,转载请标明原文链接: http://www.cnblogs.com/dolphin0520/p/3811

Java内部类this$0字段产生的一个bug

首先查看下面一段代码,我指出了问题代码的所在,读者先自己思考一下这段代码会有什么问题. 这是用clone方法完整拷贝一个二项堆(BinomialHeap)结构的代码.二项堆中包含一个内部类BinomialHeapEntry,这个内部类的对象即二项堆中的每一个结点,除了包含结点对应的关键字外,还记录父节点parent,下一个兄弟结点sibling和第一个孩子结点child三个指针.二项堆的根表通过每棵二项树根节点的sibling指针链接. cloneBinomialTree(BinomialHea

Java内部类

本文是<Java核心技术 卷1>中第六章接口与内部类中关于内部类的阅读总结. Java中的内部类(inner class)是定义在另一个类内部的类.那么内部类有什么用呢?这里主要由三个内部类存在的原因: 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据.即,如果类A中定义了类B,那么类B可以访问类A中的数据,甚至是私有数据,但类A不能访问类B中的私有数据: 内部类可以对同一个包中的其他类隐藏起来.在一个包中,定义一个类时,即使不加上访问权限关键词,这个类也是包内其他类可访问的,不

Java内部类详解(一)(转自:http://blog.csdn.net/wangpeng047/article/details/12344593)

很多人对于Java内部类(Inner Class)都十分陌生,甚至听都没听过也没有使用过,内部类在Java中其实是比较重要的一块内容,掌握好这门知识对于编程来说,犹如插上一对翅膀. 一.概念 内部类是指在一个外部类的内部再定义一个类,类名不需要和文件名相同. 对于一个名为outer的外部类和其内部定义的名为inner的内部类.编译完成后会生成outer.class和outer$inner.class两个类.所以内部类的成员变量.方法名可以和外部类的相同. 内部类可以是静态static和非静态的,

9)Java内部类(Inner Class)

内部类:不可以有静态数据,静态方法或者又一个静态内部类 内部类的优点:隐藏类的细节,内部类可以声明为私有.内部类可以访问外部类的对象(包括private) 静态内部类:可以有静态数据,静态方法或者又一个静态内部类 局部内部类:定义在一个方法或者一个代码块中的类,作用域为相应代码块 匿名内部类:该类没有名字,一般直接写为(new 接口(..))并返回一个实现了某接口的对象.   为什么需要内部类?     典型的情况是,内部类继承自某个类或实现某个接口,内部类的代码操作创建其的外围类的对象.所以你

java内部类的一些看法

java内部类, 我在看<thinking in java>的时候总感觉模棱两可的, 挣扎了好几天之后, 感觉有一部分的问题想的清楚了, 写一个随笔记录一下, 以备以后修改和查看 什么是内部类? 内部类说白了就是类套类, 在一个类里面写一个类, 内部类分为两种, 一种是普通内部类(non-static nested class), 第二种是静态内部类(static nested class),也叫嵌套类. 普通内部类还包括两种特殊的类型, 一种是局部内部类, 另一种是匿名内部类 内部类有什么作

java 内部类来将线程 代码隐藏在类中

package org.rui.thread.basic; import java.util.concurrent.TimeUnit; /** * 有时通过使用内部类来将线程 代码隐藏在类中 将会很有用,就像下面这样: * ----- * InnerThread1创建了一个扩展自thread的匿名内部类,并且在构造器中创建了这个内部类的一个实例. * 如果内部类具有你在其他方法中需要访问的特殊能力 (新方法) * 那这么做将会很有意义.但是,在大多数时候,创建线程的原因只是为了使用thread的

java 内部类与控制框架

应用程序控制框架(application framework)就是设计解决某类特殊问题的一个类,或一组类,要运用某个应用程序框架,通常是继承一个类或多个类,并覆盖这些方法.在覆盖的方法中编写代码定制应用程序框架提供的解决方案,以解决你的问题 控制框架是一类特殊应用程序框架,它用来解决响应事件的需求,主要用来响应事件的系统被称为事件驱动系统,(最常见的是GUI) 应用框架设计中,设计的关键是"变化的事物和不变的事物相互分离" //: innerclasses/controller/Eve