首页 > 代码库 > 雪习新知识:Java 内部类
雪习新知识:Java 内部类
本文出自 http://blog.csdn.net/zhaizu/article/details/49176543,转载请注明出处。
嵌套类,内部类,静态内部类,静态嵌套类。匿名类,成员类,局部类,傻傻分不清?
各种类,各种累!本文为你抽丝剥茧,庖丁解牛。娓娓道来。
首先声明一下,本文要讲的不是一个文件中面并列的两个类,而是在一个类里面定义另外一个类。
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
// 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?
本文出自 http://blog.csdn.net/zhaizu/article/details/49176543。转载请注明出处。
雪习新知识:Java 内部类