栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Android面试题集锦一

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Android面试题集锦一

1、什么是 Android DataBinding

DataBinding是谷歌官方发布的一个数据绑定的框架,是MVVM模式在Android上的一种实现,用于降低布局和逻辑的耦合性,使代码逻辑更加清晰。MVVM相对于MVP,其实就是将Presenter层替换层了ViewModel层。DataBinding能够省去我们一直以来的findViewById()步骤,大量减少Activity内的代码,数据能够单向或双向绑定到layout文件中,有助于防止内存泄漏,而且能自动进行空检测以避免空指针异常。
弊端:需要修改布局文件,布局文件代码变多,变复杂。

2、什么是Lifecycle

Google官方推出的Android Jetpack中的一个基于观察者模式实现的架构组件,用于管理Activity和Fragment的生命周期。让业务组件能够自动感知Activity和Fragment的生命周期。
使用方法:

class MainActivity : AppCompatActivity() {
    private val myLifecycle = MyLifecycle()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        lifecycle.addObserver(myLifecycle)
    }

    override fun onDestroy() {
        super.onDestroy()
        lifecycle.removeObserver(myLifecycle)
    }
}

class MyLifecycle : LifecycleObserver{

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    fun onCreate(@NonNull owner: LifecycleOwner){
        Log.i("MyLifecycle", "onCreate() ")
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun onStart(@NonNull owner: LifecycleOwner){
        Log.i("MyLifecycle", "onStart() ")
    }
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun onResume(@NonNull owner: LifecycleOwner){
        Log.i("MyLifecycle", "onResume() ")
    }
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun onPause(@NonNull owner: LifecycleOwner){
        Log.i("MyLifecycle", "onPause() ")
    }
    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun onStop(@NonNull owner: LifecycleOwner){
        Log.i("MyLifecycle", "onStop() ")
    }
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun onDestroy(@NonNull owner: LifecycleOwner){
        Log.i("MyLifecycle", "onDestroy() ")
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_ANY)
    fun onLifecycleChanged(@NonNull  owner: LifecycleOwner, @NonNull event: Lifecycle.Event){
        Log.i("MyLifecycle", "onLifecycleChanged: Event =  $event")
    }
}

原理解析(以AppCompatActivity为例):

继承关系:AppCompatActivity -》 FragmentActivity -》ComponentActivity -》LifecycleOwner

public class FragmentActivity implements LifecycleOwner{
   final LifecycleRegistry mFragmentLifecycleRegistry = new LifecycleRegistry(this);
   
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    ...//省略其他代码
    super.onCreate(savedInstanceState);//执行生命周期方法
    mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);//生命周期事件分发
    }
    
     @Override
    protected void onStart() {
        ...
        super.onStart();
        mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
        }

    @Override
    protected void onPause() {
        super.onPause();
        mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE);
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
    }
}

随着AppCompatActivity 走到不同的生命周期,除了暴露给我们的生命周期方法onCreate/onStart/…./onDestroy 等,同时,AppCompatActivity 内部的Lifecycle 对象(就是mLifecycleRegistry )还将生命周期对应的事件作为参数传给了
handleLifecycleEvent() 方法。

下面看一下LifecycleRegistry源码中的handleLifecycleEvent方法:

public class LifecycleRegistry extends Lifecycle {
...
   public void handleLifecycleEvent(@NonNull Lifecycle.Event event) {
        State next = getStateAfter(event);
        moveToState(next);
    }
...
}

首先确认一点, LifecycleRegistry 就是Lifecycle 的子类,LifecycleRegistry 同样也能通过addObserver方法注册LifecycleObserver,当LifecycleRegistry 本身的生命周期改变后(可以想象,内部一定有一个成员变量State记录当前的生命周期),LifecycleRegistry 就会逐个通知每一个注册的LifecycleObserver ,并执行对应生命周期的方法。

我们看到LifecycleRegistry 的handleLifecycleEvent() 方法:会通过getStateAfter 获取当前应处的状态并修改Lifecycle本身的State 值,紧接着遍历所LifecycleObserver 并同步且通知其状态发生变化,因此就能触发LifecycleObserver 对应的生命周期事件。

3、ListView与RecycleView有什么区别

ListView:

  • 只能垂直方向滑动。
  • 有几个默认的Adapter,分别是ArrayAdapter、CursorAdapter和SimpleCursorAdapter。
  • 拥有子Item的监听函数:AdapterView.OnItemClickListener。
  • 并不强制使用ViewHolder,如果要使用,则需要自己定义,如果不使用,ListView每次getView()时候都需要去findViewByid,会造成性能下降,滑动卡顿等,所以推荐使用ViewHolder。
  • 两级缓存。

RecycleView:

  • 支持水平和垂直方向滑动,多行多列瀑布流的方式等。
  • Adapter需要自己实现。
  • 需要自己实现接口,来实现子Item的点击事件,虽然比较麻烦,但是扩展性好。
  • 必须使用ViewHolder。
  • 四级缓存。

缓存区别:
ListView两级缓存:

  1. mActivityViews用于屏幕内ItemView快速重用
  2. mScrapViews用于缓存离开屏幕的ItemView

RecyclerView四级缓存:

  1. mChangeScrap与mAttchedScrap用于怕屏幕内itemView快速重用
  2. mCachedViews默认上限为2,即缓存屏幕外2个ItemView
  3. mViewCacheExtension用户自定义,一般不适用
  4. RecycledViewPool默认上限为5。

缓存对象不同:RecycleView缓存的是ViewHolder,ListerView缓存的是View。

4、什么是ViewHolder模式,优点是什么

没有使用ViewHolder:

使用ViewHolder:

优点:使用ViewHolder模式可以避免没有必要的findViewById()方法的调用,减少性能损耗。

5、Fragment

Android 3.0也就是API 11加入了Fragment,主要是给大屏设备上进行动态灵活的UI设计提供支持,但是更多情况下我们把Fragment作为一个可重复利用的模块组件,利用它自身的生命周期来对功能模块进行分离。

加载方式有两种:
1、静态加载直接添加Fragment到Activity的布局文件中;
2、动态的在Activity中添加Fragment。

Fragment与Activity的生命周期如下:

Fragment通信
1、在Fragment中调用Activity中的方法:通过getActivity()方法获取所附属的Activity后调用当中的方法
2、在Activity中调用fragment中的方法:采用接口回调的形式
3、在Fragment中调用Fragment中的方法:findFragmentById()拿到fragment对象
4、可以通过EventBus,liveData等方式。

replace、add、remove的区别:

  • add是把一个Fragment添加到一个容器中。一般在使用add的时候,为了避免Fragment的重复创建节省资源,会配合hide,show一起使用;
  • replace 在添加Fragment到容器之前先移除掉相同id的所有Fragment然后再添加;
  • remove 自然就是移除对应的fragment。

对fragment进行操作都会使用Fm得到一个FragmentTransaction事务对象,在一个Activity+多个Fragment的回退处理,使用hide,show进行判断切换,这样对于同级fragment用着挺不错的,但是对于比如导航栏类似的功能,多个层级的回退就不够好用,此时可以使用addToBackStack,popBackStack来实现。
addToBackStack用于记录添加fragment到回退栈。popBackStack回退到addToBackStack记录的回退栈中,即上一个页面。

commit与commitAllowingStateLoss
Activity被系统回收(界面已经不存在了),为了能在下次打开的时候恢复原来的样子,系统为我们保存界面的所有状态,这个时候再去修改界面理论上肯定是不被允许的,为了这种异常可以使用:transaction.commitAllowingStateLoss()
与commit()不同的是使用这种方法允许操作丢失界面的状态和信息。

Adapter的类型
FragmentPagerAdapter一般用于少量界面的Viewpager,划过的Fragment会一直保存在内存中不会被销毁。
FragmentStatePagerAdapter适用于界面较多的ViewPager,它会保存当前的界面以及下一个界面和上一个界面,最多保存三个,其他的会在destroyItem()方法中被销毁掉,可以节省内存占用。

ViewPage与Fragment结合适用时的懒加载问题
我们经常在使用fragment时,常常会结合viewpager使用,默认加载前两个fragment,那么我们就会遇到一个问题,就是初始化fragment的时候,会连同我们写的网络请求一起执行,这样非常消耗性能,最理想的方式是:只有用户点开或滑动到当前fragment时才进行请求网络的操作。因此,我们就产生了懒加载这样一个说法。
在Fragment中有一个setUserVisibleHint这个方法,而且这个方法是优先于onCreate()方法的,它会通过isVisivleToUser告诉我们当前Fragment是否可见,我们可以在可见的时候再进行网络加载。

setUserVisibleHint()方法被调用的时机:

  • 当fragment被创建时,setUserVisibleHint(boolean isVisibleToUser)方法会被调用,且传入参数值为false。
  • 当fragment可见时,setUserVisibleHint(boolean isVisibleToUser)方法会被调用,且传入参数值为true。
  • 当fragment由可见变为不可见时,setUserVisibleHint(boolean isVisibleToUser)方法会被调用,且传入的参数值为false。
6、单例设计模式

1、饿汉式使用时机:应用程序总是创建并使用单例实例或在创建和运行时开销不大

   //Java实现
public class SingletonDemo {
    private static SingletonDemo instance=new SingletonDemo();
    private SingletonDemo(){
 
    }
    public static SingletonDemo getInstance(){
        return instance;
    }
}
 
//Kotlin实现
object SingletonDemo
 

2、懒汉式使用时机:开销比较大,希望用到时才创建就要考虑延迟实例化 Singleton的初始化需要某些外部资源(比如网络或存储设备)

  //Java实现
public class SingletonDemo {
    private static SingletonDemo instance;
    private SingletonDemo(){}
    public static SingletonDemo getInstance(){
        if(instance==null){
            instance=new SingletonDemo();
        }
        return instance;
    }
}

//Kotlin实现
class SingletonDemo private constructor() {
    companion object {
        private var instance: SingletonDemo? = null
            get() {
                if (field == null) {
                    field = SingletonDemo()
                }
                return field
            }
        fun get(): SingletonDemo{
         return instance!!
        }
    }
}
 

3、线程安全的懒汉式

//Java实现
public class SingletonDemo {
    private static SingletonDemo instance;
    private SingletonDemo(){}
    public static synchronized SingletonDemo getInstance(){//使用同步锁
        if(instance==null){
            instance=new SingletonDemo();
        }
        return instance;
    }
}
//Kotlin实现
class SingletonDemo private constructor() {
    companion object {
        private var instance: SingletonDemo? = null
            get() {
                if (field == null) {
                    field = SingletonDemo()
                }
                return field
            }
        @Synchronized
        fun get(): SingletonDemo{
            return instance!!
        }
    }
}
 

4、双重校验锁式(Double Check)

//Java实现
public class SingletonDemo {
    private volatile static SingletonDemo instance;
    private SingletonDemo(){} 
    public static SingletonDemo getInstance(){
        if(instance==null){
            synchronized (SingletonDemo.class){
                if(instance==null){
                    instance=new SingletonDemo();
                }
            }
        }
        return instance;
    }
}
//kotlin实现
class SingletonDemo private constructor() {
    companion object {
        val instance: SingletonDemo by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
        SingletonDemo() }
    }
}

//kotlin实现 2
class SingletonDemo private constructor(private val property: Int) {
    companion object {
        @Volatile private var instance: SingletonDemo? = null
        fun getInstance(property: Int) =
                instance ?: synchronized(this) {
                    instance ?: SingletonDemo(property).also { instance = it }
                }
    }
}
 
 

5、静态内部类

//Java实现
public class SingletonDemo {
    private static class SingletonHolder{
        private static SingletonDemo instance=new SingletonDemo();
    }
    private SingletonDemo(){
        System.out.println("Singleton has loaded");
    }
    public static SingletonDemo getInstance(){
        return SingletonHolder.instance;
    }
}
//kotlin实现
class SingletonDemo private constructor() {
    companion object {
        val instance = SingletonHolder.holder
    }
 
    private object SingletonHolder {
        val holder= SingletonDemo()
    }
}
 

6、枚举

    //Java实现
    public class Single {
        private Single(){}
        public enum SingleEnum {
            singleHandler;
            private Single single;
            private SingleEnum () {
                single = new Single();
            }
            public Single getSingle() {
                return single;
            }
        }
        public static Single getInstacne() {
            return SingleEnum.singleHandler.getSingle();
        }
    }
7、什么是匿名内部类,它有什么特征
  • 匿名内部类也就是没有名字的内部类
  • 正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写
  • 但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口
  • 匿名内部类中是不能定义构造函数的

使用的形参为何要为final
我们给匿名内部类传递参数的时候,若该形参在内部类中需要被使用,那么该形参必须要为final。也就是说:当所在的方法的形参需要被内部类里面使用时,该形参必须为final。

为什么必须要为final呢?

  • 首先我们知道在内部类编译成功后,它会产生一个class文件,该class文件与外部类并不是同一class文件,仅仅只保留对外部类的引用。当外部类传入的参数需要被内部类调用时,从java程序的角度来看是直接被调用
  • 在内部类中的属性和外部方法的参数两者从外表上看是同一个东西,但实际上却不是,所以他们两者是可以任意变化的,也就是说在内部类中我对属性的改变并不会影响到外部的形参,然而这从程序员的角度来看这是不可行的,毕竟站在程序的角度来看这两个根本就是同一个,如果内部类改变了,而外部方法的形参却没有改变这是难以理解和不可接受的,所以为了保持参数的一致性,就规定使用final来避免形参的不改变。
  • 简单理解就是,拷贝引用,为了避免引用值发生改变,例如被外部类的方法修改等,而导致内部类得到的值不一致,于是用final来让该引用不可改变。

为什么要有内部类

  1. 内部类是为了更好的封装,把内部类封装在外部类里,不允许同包其他类访问
  2. 内部类中的属性和方法即使是外部类也不能直接访问
  3. 相反内部类可以直接访问外部类的属性和方法,即使private
  4. 利于回调函数的编写。 PS:回调函数是函数的迭代
  5. 当描述事物,如身体里的大脑时,大脑在身体内部,可以通过内部类直观描述

内部类特点

  1. 内部类对象不仅指向该内部类,还指向实例化该内部类的外部类对象的内存。
  2. 内部类和普通类一样可以重写Object类的方法,如toString方法;并且有构造函数,执行顺序依旧是先初始化属性,再执行构造函数
  3. 在编译完之后,会出现(外部类.class)和(外部类﹩内部类.class)两个类文件名。
  4. 内部类可以被修饰为private,只能被外部类所访问。事实上一般也都是如此书写。
  5. 内部类可以被写在外部类的任意位置,如成员位置,方法内。

内部类的创建:
Java代码创建方式:

public class Outer {

  public class Inner {
    public void print(String str) {
       System.out.println(str);
      }
    }
}

Kotlin必须使用inner声明:

class Outer {
    inner class Inner{
    
    }
}
8、String中“==”与equals()的区别
  • “==”比较的是内存中存放的位置
  • equals()比较的是字符序列,比较的是值
 @Test
    public void testString() {
        String str1 = "a" + "b"+ "c";
        String str2 = "abc";
        String str3 = new String("abc");
        System.out.println(str1 == str2);
        System.out.println(str1 == str3);
        System.out.println(str2 == str3);
        System.out.println(str1.equals(str2));
        System.out.println(str2.equals(str3));
        System.out.println(str1.equals(str3));
        System.out.println(str1.hashCode()+" "+str2.hashCode()+" "+str3.hashCode());
    }

输出结果:

9、为什么说String是不可变的
  • 通过代码验证创建一个String变量str,多次给str重新赋值时,每次赋值后str的hashCode都不一样,由此可知String是不可变的,重新赋值时会创建一个新的str对象。代码如下:
    @Test
    public void testString2() {
        String str = new String("abc");
        System.out.println(str.hashCode());
        str += "a";
        System.out.println(str.hashCode());
        str += "b";
        System.out.println(str.hashCode());
        String str2="abcab";//内容与str一样,所以str与str2是同一个对象
        System.out.println(str2.hashCode()+" "+str.hashCode());
    }

输出结果:

  • String的源中String类定义的是final类型,它的长度count、UID和serialPersistentFields内容都是final修饰的,由此可值String是不可变的。
public final class String implements java.io.Serializable, Comparable, CharSequence {
 	private final int count;
 	private static final long serialVersionUID = -6849794470754667710L;
    private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];
}
10、什么是内存泄漏,JAVA是如何处理它的
  • 内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
  • 产生的原因:一个长生命周期的对象持有一个短生命周期对象的引用
  • 通俗讲就是该回收的对象,因为引用问题没有被回收,最终会产生OOM。
    出现内存泄漏的情况,JAVA不会主动去处理,需要开发者自己去排查存在内存泄漏的情况,并解决。
11、 Java回收机制,如何减少OOM的概率

GC判断对象存活的方式:
可达性分析,如果一个对象的引用链上没有GC Root,则表示此对象可被回收,反之无法在GC中被回收。
在Java中,可作为GC Roots的对象包括:

  • 方法区:类静态属性的对象;
  • 方法区:常量对象;
  • 虚拟机栈(本地变量表)中的对象;
  • 本地方法栈JNI(Native方法)中的对象。

如何减少OOM的概率:

  • 尽可能少的发生内存泄漏
  • 尽可能不在循环中申请内存
  • 尽可能不在调用次数多的函数中申请内存。
12、JAVA中强引用、软引用、弱引用和虚引用
  • 强引用:Object obj = new Object()。
  • 软引用SoftReference:GC时不会回收,内存不足时回收,主要用于内存缓存时使用。
  • 弱引用WeakReference:GC到来时回收,用于被回收对应用没影响的对象使用。
  • 虚引用PhantomReference:GC回收时可得到一个通知。
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/305992.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号