一.磨叽磨叽Java值传递与引用传递
“在Java里面参数传递都是按值传递”
即:按值传递是传递的值的拷贝,按引用传递其实传递的是引用的地址值,所以统称按值传递。
简单的说,基本数据类型是按值传递的,方法的实参是一个原值的复本。类对象是按对象的引用地址(内存地址)传递地址的值,那么在方法内对这个对象进行修改是会直接反应在原对象上的(或者说这两个引用指向同一内存地址)。注意String也是值传递。
public static void main(String[] args) {
String x = new String("now");
User user = new User("now");
change(x);
System.out.println("1:"+x);
change(user);
System.out.println("2:"+user.name);
User user1 = new User("now");
System.out.println("3:"+user1 .name);
}
public static void change(String x) {
x = "even";
}
public static void change(User x) {
x.name = "even";
}
public static void change(User x) {
x = new User("even");
}
得出的结果
1:even
2:even
3:now
二.回归正题,聊聊Java内部类的这些事
Java允许在一个类里面定义另一个类,类里面的类就是内部类,也叫做嵌套类
简单的内部类如下:
class OuterClass {
class InnerClass{
}
}
经常用到的内部类:
public class PrivateToOuter {
Runnable mRunnable = new Runnable(){
private int x=10;
@Override
public void run() {
System.out.println(x);
}
};
}
以上代码mRunnable让人并不觉得是内部类,它并不像InnerClass那样形象,但是其实以下句柄实现一个继承Runable的类,也就是自定义了一个类,那么明显它就是一个内部类。其实它是属于内部类一种:匿名内部类Anonymous Inner Class
{
private int x=10;
@Override
public void run() {
System.out.println(x);
}
}
内部类需要注意的以下几点:
(1)内部类的获取
内部类可以访问包装他的外部类的所有,方法和属性,包括私有方法。但是对其他类来说,它是隐藏的,哪怕对同一个包下面其他类来说,内部类都是隐藏的,也就是说,如果要访问内部类,就必须先要得到他的外部包装类,得到那个句柄之后在new 内部类,就可以调用他的方法了,如下:
OuterClass outer = new OuterClass();
InnerClass innerClass = outer.new InnerClass();
所以内部类是可以很好的解决单继承的问题。如果对于private在此逻辑为何失效的话,可以查看细话Java:"失效"的private修饰符
(2)静态内部类
如果用static 将内部内静态化,那么内部类就只能访问外部类的静态成员变量,具有局限性
class Out {
private static int age = 12;
private int time = 14;
static class In {
public void print() {
System.out.println(age);
}
}
}
public class Demo {
public static void main(String[] args) {
Out.In in = new Out.In();
in.print();
}
}
也就是说in只能访问age,而非time
(3)私有内部类
如果一个内部类只希望被外部类中的方法操作,那么可以使用private声明内部类
class Out {
private int age = 12;
private class In {
public void print() {
System.out.println(age);
}
}
public void outPrint() {
new In().print();
}
}
public class Demo {
public static void main(String[] args) {
//此方法无效
/*
Out.In in = new Out().new In();
in.print();
*/
Out out = new Out();
out.outPrint();
}
}
需要特别告诉大家重要的point就是:
在内部类构造的时候,会将外部类的引用传递进来,并且作为内部类的一个属性,所以内部类会持有一个其外部类的引用。
在Java中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用。静态的内部类不会持有外部类的引用。所以point3中的内部类是无法获取到外部类的成员变量与方法。
回归到android:
public class SampleActivity extends Activity {
private final Handler handler= new Handler() {
@Override
public void handleMessage(Message msg) {
// ...
}
}
}
这是我们获取handler实例经常所做的操作,但是回归到point得知,handler的匿名内部类中有SampleActivity的引用,所以这是有可能导致OOM的隐患,比如以下代码:
public class SampleActivity extends Activity {
private final Handler mLeakyHandler = new Handler() {
@Override
public void handleMessage(Message msg) { // ... }
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mLeakyHandler.postDelayed(new Runnable() {
@Override
public void run() { /* ... */ }
}, 1000 * 60 * 10);
finish();
}
}
SampleActivity对象已经finish,但是mLeakyHandler的消息要在10分钟后执行操作,而Handler是一个匿名内部类的实例,其持有外面的SampleActivity的引用(而且Runable也是匿名内部类,其内部也是持有SampleActivity的引用),所以这导致了SampleActivity无法回收,进行导致SampleActivity持有的很多资源都无法回收,这就是我们常说的内存泄露。解决方法如下:
public class SampleActivity extends Activity {
private static class MyHandler extends Handler {
private final WeakReference<SampleActivity> mActivity;
public MyHandler(SampleActivity activity) {
mActivity = new WeakReference<SampleActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
SampleActivity activity = mActivity.get();
if (activity != null) { // ... }
}
}
private final MyHandler mHandler = new MyHandler(this);
private static final Runnable sRunnable = new Runnable() {
@Override
public void run() { /* ... */ }
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler.postDelayed(sRunnable, 1000 * 60 * 10);
finish();
}
}