雪习新知识: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
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?
本文出自 http://blog.csdn.net/zhaizu/article/details/49176543。转载请注明出处。
雪习新知识:Java 内部类的更多相关文章
- 学习android学习必备的java基础知识--四大内部类
学习android必备的java基础知识--四大内部类 今天学习android课程,因为我的主专业是JAVA,但是兴趣班却有这其他专业的同学,学习android 需要具备一些java的基础知识,因此就 ...
- java内部类基础知识
一.java内部类具体分四大类 1.成员内部类 2.静态内部类 3.局部内部类 4.匿名内部类 1.成员内部类 :作为类的成员,存在于类中 //成员内部类可以调用外部类的所有 ...
- Java内部类详解
Java内部类详解 说起内部类这个词,想必很多人都不陌生,但是又会觉得不熟悉.原因是平时编写代码时可能用到的场景不多,用得最多的是在有事件监听的情况下,并且即使用到也很少去总结内部类的用法.今天我们就 ...
- [转] Java内部类详解
作者:海子 出处:http://www.cnblogs.com/dolphin0520/ 本博客中未标明转载的文章归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置 ...
- Java内部类this$0字段产生的一个bug
首先查看下面一段代码,我指出了问题代码的所在,读者先自己思考一下这段代码会有什么问题. 这是用clone方法完整拷贝一个二项堆(BinomialHeap)结构的代码.二项堆中包含一个内部类Binomi ...
- 20175227张雪莹 2018-2019-2 《Java程序设计》第六周学习总结
20175227张雪莹 2018-2019-2 <Java程序设计>第六周学习总结 教材学习内容总结 第七章 内部类与异常类 内部类:在一个类中定义另一个类:包含内部类的类为外嵌类 内部类 ...
- Java内部类详解 2
Java内部类详解 说起内部类这个词,想必很多人都不陌生,但是又会觉得不熟悉.原因是平时编写代码时可能用到的场景不多,用得最多的是在有事件监听的情况下,并且即使用到也很少去总结内部类的用法.今天我们就 ...
- 死磕Java内部类
Java内部类,相信大家都用过,但是多数同学可能对它了解的并不深入,只是靠记忆来完成日常工作,却不能融会贯通,遇到奇葩问题更是难以有思路去解决.这篇文章带大家一起死磕Java内部类的方方面面. 友情提 ...
- java 内部类 *** 最爱那水货
注: 转载于http://blog.csdn.net/jiangxinyu/article/details/8177326 Java语言允许在类中再定义类,这种在其它类内部定义的类就叫内部类.内部类又 ...
随机推荐
- xshell 连接redis
当我们安装好redis 在本地测试启动,存,取都正常,但是无法远程连接redis. 在redis.conf的文件默认配置中有一配置是 bind 127.0.0.1,表示绑定访问该redis的地址只能是 ...
- BZOJ3129 [Sdoi2013]方程 【扩展Lucas】
题目 给定方程 X1+X2+. +Xn=M 我们对第l..N1个变量进行一些限制: Xl < = A X2 < = A2 Xn1 < = An1 我们对第n1 + 1..n1+n2个 ...
- cocoapods的安装使用
本文非原创,只是看了别人的教程,自己做了下笔记 转载cocoapods其他详细教程 mac快速安装 由于天朝的那堵墙的阻挡,我们需要用taobao的镜像 以下操作在终端进行 gem sources - ...
- 321. Create Maximum Number 解题方法详解
321. Create Maximum Number 题目描述 Given two arrays of length m and n with digits 0-9 representing two ...
- [暑假集训--数论]poj2262 Goldbach's Conjecture
In 1742, Christian Goldbach, a German amateur mathematician, sent a letter to Leonhard Euler in whic ...
- JavaScript (JS)基础:ECMAScript 浅析 (含Math基本方法解析)
浏览器端JavaScript的组成 ECMAScript:语法规范 DOM:Document Object Model 文档对象模型,操作页面元素 BOM:Browser Object Model ...
- float 及 overflow 的理解
1.CSS 盒子模型: 2.float 支持属性:left right none inherit(部分支持) (1)float 属性影响范围:对紧随其后的块儿级元素起作用. (2)清除浮动常用方法:在 ...
- POJ1111 Image Perimeters
Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 8790 Accepted: 5260 Description Techn ...
- Visual Studio 试用版的评估期已经结束【解决方案】
原文发布时间为:2010-01-09 -- 来源于本人的百度文章 [由搬家工具导入] Visual Studio 试用版的评估期已经结束…… 解决。2010年1月9日0:02:50,本人亲自尝试,可行 ...
- HTML title属性换行显示的方法
原文发布时间为:2009-04-22 -- 来源于本人的百度文章 [由搬家工具导入] 解决的方法有两种: 1.将title属性分成几行来写,例如:<a href=#" title=&q ...