CVTE面试总结

Activity和Service通信

/*************************Service代码****************************************/
public class LocalService extends Service {  
    private final IBinder binder = new LocalBinder();  

    public class LocalBinder extends Binder {  
        LocalService getService() {  
            return LocalService.this;  
        }  
    }  

    public IBinder onBind(Intent intent) {  
        return binder;  
    }  
}  

/*****************************Activity代码*************************************/
public class BindingActivity extends Activity {  
    LocalService localService;  

    private ServiceConnection mConnection = new ServiceConnection() {  
        public void onServiceConnected(ComponentName className,IBinder localBinder) {  
            localService = (LocalBinder) localBinder.getService();  
        }  
        public void onServiceDisconnected(ComponentName arg0) {  
            localService = null;  
        }  
    }; 

    protected void onStart() {  
        super.onStart();  

        Intent intent = new Intent(this, LocalService.class);  
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);  
    }  

    protected void onStop() {  
        super.onStop();  
        unbindService(mConnection);  
    }   

    public void printRandomNumber{  
          int num = localService.getRandomNumber();  
       System.out.println(num);
    }
}

Activity能进行绑定得益于Service的接口onBind()。

先回顾下使用context.bindService()启动Service,会经历:

context.bindService()->onCreate()->onBind()->Service running
onUnbind() -> onDestroy() ->Service stop

Service和Activity的连接可以用ServiceConnection来实现,需要实现一个新的ServiceConnection,重写onServiceConnected和onServiceDisconnected方法。执行绑定,调用bindService方法,传入一个选择了要绑定的Service的Intent(显式或隐式)和一个你实现了的ServiceConnection实例。一旦连接建立,你就能通Service的接口onBind()得到serviceBinder实例进而得到Service的实例引用。一旦Service对象找到,就能得到它的公共方法和属性。但这种方式,一定要在同一个进程和同一个Application里。

一个组件绑定Service后,其他组件还能绑定吗?

一个service可以绑定多个组件。当组件与Service绑定后,Service的生命周期就和组件相关了,需要调用unBindService()解绑。且直到所有组件都解绑了,系统才会销毁Service。

如果是组件startService启动的Service,需要用户手动stopService()或者在Service执行完工作以后stopSelf(),否则即便组件被销毁,Service也会一直存在。

GC如何识别垃圾对象?

首先看一段话,摘自《深入理解Java虚拟机》:

在主流的商用程序语言实现中,都是通过可达性分析来判定对象是否存活。该算法的基本思想就是通过一系列的被称为“GC Roots”的对象作为起始点,从这些节点向下搜索,搜索过程中所走过的路径成为“引用链”,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。

在Java中,可作为GC Roots的对象包括:
虚拟机栈中引用的对象。
方法区中类静态属性引用的对象。
方法区中常量引用的对象。
本地方法栈中JNI引用的对象。

从引用可达性的角度分析内存泄露

首先参照 GC如何识别垃圾对象,然后我们以Context为例,看下内存泄露,不过在这之前我们先看一个正常案例:

public class MyActivity extends Activity{  

    private ActivityManager mManager = null;  

    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        //...
        mManager = new ActivityManager(this);  
    }  
public class ActivityManager {  
    private Context mContext = null;  

    public ActivityManager (Context context) {  
        mContext = context;  
    }  
} 

很显然,MyActivityActivityManager是相互依赖的,这种情况会不会造成内存泄露呢?不会!

因为两个类虽然属于互相引用,但是当这个Activity销毁以后,这两个类都处于GC Roots不可达到的区域,这种情况在GC的时候这两个类是会被同时回收掉的,如同下图中的object5、6、7。

这里写图片描述

而如果我们把ActivityManager改成单例:

public class ActivityManager{  

    private Context mContext = null;  

    private static ActivityManager mInstance = null;  

    public static ActivityManager getInstance(Context context) {  
        if (mInstance == null) {  
            synchronized (ActivityManager.class) {  
                if (mInstance == null) {  
                    mInstance = new ActivityManager(context);  
                }  
            }  
        }  
        return mInstance;  
    }  

    private ActivityManager(Context context){  
        mContext = context;  
    }  
}  

MyActivity对应的改动这里就不写了。此时ActivityManager的生命周期变成和app相关了,即便MyActivityfinish掉,GC时,它们也不会被回收。因为ActivityManager仍然引用了MyActivity(Context),这里GC Roots就是上面提到的第二条:方法区中类静态属性引用的对象。所以MyActivity仍然无法被系统回收。

一个对象有几个锁?

对象锁:java的所有对象都含有1个互斥锁,这个锁由JVM自动获取和释放。线程进入synchronized方法的时候获取该对象的锁,当然如果已经有线程获取了这个对象的锁,那么当前线程会等待;synchronized方法正常返回或者抛异常而终止,JVM会自动释放对象锁。这里也体现了用synchronized来加锁的1个好处,方法抛异常的时候,锁仍然可以由JVM来自动释放。

类锁:对象锁是用来控制实例方法之间的同步,类锁是用来控制静态方法(或静态变量互斥体)之间的同步。其实类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的。我们都知道,java类可能会有很多个对象,但是只有1个Class对象,也就是说类的不同实例之间共享该类的Class对象。Class对象其实也仅仅是1个java对象,只不过有点特殊而已。由于每个java对象都有1个互斥锁,而类的静态方法是需要Class对象。所以所谓的类锁,不过是Class对象的锁而已。获取类的Class对象有好几种,最简单的就是MyClass.class的方式。

对象的引用是在堆中还是栈中?

Java中分配堆内存是自动初始化的。Java中所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在堆栈中分配。也就是说在建立一个对象时从两个地方都分配内存,在堆中分配的内存实际建立这个对象,而在栈中分配的内存只是一个指向这个堆对象的指针(引用变量)而已。 在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。

在堆中产生了一个数组或对象后,还可以在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。

引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其作用域之外后被释放。而数组和对象本身在堆中分配,即使程序运行到使用 new 产生数组或者对象的语句所在的代码块之外,数组和对象本身占据的内存不会被释放,数组和对象在没有引用变量指向它的时候,才变为垃圾,不能在被使用,但仍然占据内存空间不放,在随后的一个不确定的时间被垃圾回收器收走(释放掉)。

Java为什么会设计反射?

首先反射,就是“反其道”去创建一个对象,或者调用类中的方法,基本概念不再讲,这里只说反射存在的必要性。

Java运行时仍然拥有类型信息,它包含了这个类一切:它有哪些字段、哪些方法,各是何种保护级别等等,还有这个类依赖于哪些类。在Java中,类信息以对象的形式存放,这些对象是一种元对象,它们的类型就是Class。拥有了这些信息,无论是动态创建对象还是调用某些方法都是轻而易举的,这就给反射提供了存在的可能性。

通过反射机制,我们在程序运行过程中,用类的元信息实例化对象。而在设计代码阶段,我们完全可以无视“它是什么”,这不正符合,程序开发过程中,我们一直强调的低耦合嘛。比如设计模式中的工厂模式,不用反射时,是这样:

interface Fruit {
    public void eat() ;
  }
    class Apple implements Fruit {
        public void eat() {
            System.out.println("吃苹果。");
        }
    }
    classFactory {
        public static Fruit getInstance(String className) {
            if("apple".equals(className)){
                return new Apple() ;
            }
            return null;
        }
    }
    public class FactoryDemo {
        public static void main(String[] args) {
            Fruit f = Factory.getInstance("apple") ;
            f.eat() ;
        }
    }

此时,当继续扩展时,每次都要改代码,而如果用了反射:

interface Fruit {
    public void eat() ;
}
class Apple implements Fruit {
    public void eat() {
        System.out.println("吃苹果。");
    };
}
class Orange implements Fruit {
    public void eat() {
        System.out.println("吃橘子。");
    };
}
class Factory {
    public static Fruit getInstance(String className) {
        Fruit f = null;
        try{
            f = (Fruit) Class.forName(className).newInstance() ;
        } catch(Exception e) {
            e.printStackTrace();
        }
        return f ;
    }
}
public class FactoryDemo {
    public static void main(String[] args) {
        Fruit f = Factory.getInstance("cn.mldn.demo.Orange") ;
        f.eat() ;
    }
}

这时,这个时候即使增加了接口的子类,工厂类照样可以完成对象的实例化操作,这个才是真正的工厂类,可以应对于所有的变化,这样的代码无疑更加优雅,也更省事。

反射机制还广泛运用在框架、注解和一些“黑科技”中,用来动态加载代码。深究Java设计反射的初衷,我认为可能更多的还是更方便、更优雅、更“Don’t repeat yourself”吧。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值