美文网首页
Android面试知识点(一)*

Android面试知识点(一)*

作者: 书虫大王X | 来源:发表于2021-02-19 22:31 被阅读0次
1、操作系统的内存分级

早期的内存使用机制:

  • 早期的内存使用机制:程序是直接运行在整个运行在内存上的。缺点:

    1、进程地址空间不隔离。因为是直接访问物理地址,所有恶意进程可以随意修改其他进程的数据
    2、内存使用率低。程序是整个运行在内存中的,所以当以运行其他程序时而内存容量又不够时,必须将选择内存中的某个进程将其置换出,然后运行目标进程。
    3、程序运行的地址不确定。当内存中的剩余空间可以满足程序C的要求后,操作系统会在剩余空间中随机分配一段连续的20M大小的空间给程序C使用,因为是随机分配的,所以程序运行的地址是不确定的。

内存分段:

  • 分段内存机制:运用了虚拟内存的机制。在进程和物理地址之间增加一个中间层,利用一种间接的地址访问方法访问物理内存。按照这种方法,程序中访问的内存地址不再是实际的物理内存地址,而是一个虚拟地址,然后由操作系统将这个虚拟地址映射到适当的物理内存地址上。这样,只要操作系统处理好虚拟地址到物理内存地址的映射,就可以保证不同的程序最终访问的内存地址位于不同的区域,彼此没有重叠,就可以达到内存地址空间隔离的效果。(因为每个进程都有一片虚拟内存空间,但是每个进程的虚拟内存映射到的物理地址范围是不同的,所以实现了地址隔离的效果)

  • 当创建一个进程时,操作系统会为该进程分配一个4GB大小的虚拟进程地址空间。之所以是4GB,是因为在32位的操作系统中,一个指针长度是4字节,而4字节指针的寻址能力是从0x00000000~0xFFFFFFFF,最大值0xFFFFFFFF表示的即为4GB大小的容量。

  • 在Windows系统下,这个虚拟地址空间被分成了4部分:NULL指针区、用户区、64KB禁入区、内核区

  • 虚拟内存:假设有两个进程A和B,进程A所需内存大小为10M,其虚拟地址空间分布在0x00000000到0x00A00000,进程B所需内存为100M,其虚拟地址空间分布为0x00000000到0x06400000。那么按照分段的映射方法,进程A在物理内存上映射区域为0x00100000到0x00B00000,,进程B在物理内存上映射区域为0x00C00000到0x07000000。于是进程A和进程B分别被映射到了不同的内存区间,彼此互不重叠,实现了地址隔离。(从虚拟内存到物理内存的映射是有操作系统自己完成的) image.png
  • 内存分段解决了内存隔离和程序运行地址不确定的问题,但是内存使用效率低的问题还是没有解决。

内存分页:

  • 程序的运行有局部性特点,在某个时间段内,程序只是访问程序的一小部分数据,也就是说,程序的大部分数据在一个时间段内都不会被用到。基于这种情况,人们想到了粒度更小的内存分割和映射方法,这种方法就是分页(Paging)。
  • 分页的基本方法是,将地址空间分成许多的页。每页的大小由CPU决定,然后由操作系统选择页的大小。目前Inter系列的CPU支持4KB或4MB的页大小,而PC上目前都选择使用4KB。
  • 分页的思想是程序运行时用到哪页就为哪页分配内存,没用到的页暂时保留在硬盘上。当用到这些页时再在物理地址空间中为这些页分配内存,然后建立虚拟地址空间中的页和刚分配的物理内存页间的映射。
  • PE文件:可执行文件(一些编译链接好的数据和指令的集合)。它网内存中装载的单位是页。
2、newInstance与new的区别:
  • newInstance()也是用来创建新的对象。newInstance():弱类型,效率低,只能调用无参构造
  • new():强类型,高效率,能调用任何public构造器
3、反射:
  • 要使用反射必须先得到对应的类文件对象,也就是class类的对象
  • 反射可以提高程序的灵活性;屏蔽掉实现的细节,让使用者更加方便好用
  • 应用:在一个工厂方法中,当每增加一个需要它构建的类,按照常规的方法,都需要修改一次工厂方法,但是修改代码是很麻烦的,还容易出错;使用反射方法可以避免修改原有代码,更改配置就行,例:
  • 未使用反射:
interface fruit{  
    public abstract void eat();  
}  
    
class Apple implements fruit{  
    public void eat(){  
        System.out.println("Apple");  
    }  
}  
    
class Orange implements fruit{  
    public void eat(){  
        System.out.println("Orange");  
    }  
}  
    
// 构造工厂类:以后我们在添加其他的实例的时候修改工厂类就行了  
class Factory{  
    public static fruit getInstance(String fruitName){  
        fruit f=null;  
        if("Apple".equals(fruitName)){  
            f=new Apple();  
        }  
        if("Orange".equals(fruitName)){  
            f=new Orange();  
        }  
        return f;  
    }  
}  
class hello{  
    public static void main(String[] a){  
        fruit f=Factory.getInstance("Orange");  
        f.eat();  
    }  
}  
  • 使用反射:
interface fruit{  
    public abstract void eat();  
}  
   
class Apple implements fruit{  
    public void eat(){  
        System.out.println("Apple");  
    }  
}  
   
class Orange implements fruit{  
    public void eat(){  
        System.out.println("Orange");  
    }  
}  
   
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;  
    }  
}  
class hello{  
    public static void main(String[] a){  
        fruit f=Factory.getInstance("Reflect.Apple");  
        if(f!=null){  
            f.eat();  
        }  
    }  
}  
4、Android的系统、APP启动过程
5、push机制:
  • APP PUSH推送机制

  • APP PUSH的定义为在手机终端锁屏状态下通知栏展示或在操作前台顶端弹出的消息通知,点击后可唤起对应的APP,并在APP内跳转到指定页面。

  • APP分类:从应用的功能来划分,主要分为三类应用,第一类是IM类APP(即时通讯类APP),如微信、QQ等;第二类是新闻资讯类,如华尔街见闻等;其余暂归为为工具类,比如支付宝、美团等。

  • Android手机的白名单:清理进程的时候予以忽略

  • 推送通道一般分为三类:厂商通道、第三方推送服务平台、长连接
    1、 厂商通道:是手机终端厂商推出的推送服务,通过接入厂商SDK,内部服务端可以将消息推送到手机系统的服务端,再下发至客户端内部的厂商SDK,由操作系统进行相应展示,点击后唤起相应APP,这样可以避免APP进程被杀死后消息无法触达用户,因此触达率较高。
    2、第三方推送平台:是推送服务公司自己搭建相关的消息服务。并且各个APP使用了同一个平台的推送服务时,客户端都是集成同一个第三方推送平台的SDK,因此形成了一个推送联盟,当联盟中的其中一个APP的消息进程没有被杀死的时候,其他的APP也可以利用进行通知用户,形成了相互唤起,提高触达率。
    3、长连接:是建立手机与服务端的一条链路进行消息数据推送,通过长连接也可以进行APP状态监控,但完全由长连接推送且保证触达的稳定,需要投入的研发资源较多,且需尽量避免自己的长连接进程不要被操作系统杀死。
    长连接就是建立手机与服务端的一条链路进行消息数据推送,通过长连接也可以进行APP状态监控,但完全由长连接推送且保证触达的稳定,需要投入的研发资源较多,且需尽量避免自己的长连接进程不要被操作系统杀死。

  • 推送时客户端的PUSH SDK均会根据用户的设备号生成一个对应关系的TOKEN。

8、数据的位数:
  • bit是数据的最小单位,也可以称之为位,例如形容计算机的CPU是64位的,就是说该计算机一次最多可以处理64bit的数据
  • bit存储0或1
  • 8bit是一个字节(B)
  • “位”(bit)是存储器的最小存储单位,一位可存储一位二进制数,8位二进制代码称为一个字节,字节是计算机中数据处理和存储容量的基本单位。一个存储单元中存入的信息称为一个“字”,一个字所包含的二进制数的位数称为“字长”。 一个存储单元可存储一串二进制代码,称这串二进制代码为一个存储字,这串二进制代码的个数叫存储字长。

9、atomic的实现机制

5、kotlin相对于Java有什么优缺点:
  • 优点:

1、在Kotlin语言中,类终于不再是一等公民。Kotlin语言开始支持面向过程编程,Kotlin语言中可以声明全局函数、内联函数等,还支持函数嵌套,使用函数作为方法参数等操作。对于一些简单的操作,新建一个类去处理,的确有时候是一个让人头疼的问题,Kotlin语言让我们摆脱了这一尴尬的现状。
2、数据类:我们常常要不断写一些Model类,不断地使用开发工具生成set/get方法。Data Class就是为简化这个操作而生的,数据类会自动生成set/get方法,而不用显式生成set/get方法
3、Kotlin和Java语言可以实现完全地互同调用,Kotlin最终也会编译成Java字节码。
4、完全兼容Java
5、Null safe(空指针安全)
6、支持lambda表达式(比Java8更好)
7、支持扩展
8、体验一致的开发工具链
9、相对于java更简洁
10、对比Java,Kotlin的优点和缺点

6、面向对象六大原则:

单一职责原则——SRP
开闭原则——OCP
里式替换原则——LSP
依赖倒置原则——DIP
接口隔离原则——ISP
迪米特原则——LOD

8、组合(引用)跟继承的使用场景区别:
  • 组合是通过对现有对象进行拼装即组合产生新的具有更复杂的功能。

  • 继承与组合的区别与联系:继承与组合都是面向对象中代码复用的方式。在继承中:父类的内部细节对子类可见,其代码属于白盒式的复用;而组合中,对象之间的内部细节不可见,其代码属于黑盒式复用。继承在编码过程中就要指定具体的父类,其关系在编译期就确定,而组合的关系一般在运行时确定。继承强调的是is-a的关系,而组合强调的是has-a的关系。

  • 继承的优点:

支持扩展,通过继承父类实现,但会使系统结构较复杂
易于修改被复用的代码

  • 继承的缺点:

代码白盒复用,父类的实现细节暴露给子类,破坏了封装性
当父类的实现代码修改时,可能使得子类也不得不修改,增加维护难度。
子类缺乏独立性,依赖于父类,耦合度较高
不支持动态拓展,在编译期就决定了父类

  • 组合的优点:

代码黑盒复用,被包括的对象内部实现细节对外不可见,封装性好。
整体类与局部类之间松耦合,相互独立。

支持扩展
每个类只专注于一项任务
支持动态扩展,可在运行时根据具体对象选择不同类型的组合对象(扩展性比继承好)

  • 组合的缺点:

创建整体类对象时,需要创建所有局部类对象。导致系统对象很多。

  • 什么情况下使用继承,什么情况下使用组合

从前面的分析看,组合的优点明显多于继承,再加上java中仅支持单继承,所以:
除非两个类之间是is-a的关系,否则尽量使用组合。

9、线程池的优点:

重用线程池中的线程,避免因为线程的创建和销毁带来性能开销。
能有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象。
能够对线程进行管理,并提供定时执行以及定间隔循环执行等功能。

10、tcp三次握手的目的:

三次握手的目的是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号,交换 TCP 窗口大小信息。当仅仅是两次握手时,服务器端不能知道自己发送给客户端的消息是否被客户端收到。

为什么不是两次:

存在这种情况:第一次连接请求报文由于网络节点长时间滞留了,导致延误到连接释放后的某个时间才到达 Server。这时 Server 会再次给 Client 发送确认报文(第二次握手),但是 Client 进程程序并不会理睬确认报文,因为 Client 没有发送连接请求。现在假如没有第三次握手就会建立连接,那么这次滞后的连接请求报文就会导致 TCP 误建立连接,而 Client 却不知已经建立连接,并不会发送数据给 Server,这样 Server 就会一直处于等待状态,这样就白白浪费了 Server 的很多资源。但有了第三次握手就会避免这种问题的发生,虽然延迟的连接请求发送到了 Server,但 Client 不会处理 Server 的确认报文,也不会再次发送确认请求报文,这样 Server 就知道了 Client 并没有真正想建立连接。所以不能是两次握手。

11、java泛型:
  • 在创建 对象、调用方法的时候才明确具体的类型,意思就是在编写的时候是不知道用户在使用的时候会传入什么类型的对象、数据。使用泛型的好处是代码更加简洁、更加健壮(不需要做类型的强转化,不会报类型强转错误)
    应用场景:操作集合的时候会用到(系统自己实现的集合操作方法也是使用的泛型)。
13、Unix下IO模型:
  • 阻塞lO、非阻塞lO、IO复用、信号驱动以及异步I/O。在开发中碰得最多的就是阻塞IO、非阻塞IO以及IO复用。

14、防止死锁的方法:

  • 尽量缩小锁的范围
  • 给锁添加顺序
  • 使用定时锁
  • 死锁检查(一种依靠算法机制来实现的死锁预防机制)
  • 银行家算法

解除死锁的方法:

1、资源剥夺方法
2、撤销进程
3、进程回退法

常见的网络状态码:

  • 常见常用的网络状态码
  • 503 Service Unavailable 服务器处于超载或者故障状态。如果事先得知何时可以解决故障,可以将时间写入Retry-after首部字段再返回给客户端。
  • 403 Forbidden 请求资源的访问被服务器拒绝。服务器没必要给出拒绝的理由。

Android程序去手机相册获取图片:

1、跳转到相机,选择一张图片后返回,然后回到onActivityResult方法中
1、定义一个资源标识符接收onActivityResult方法传递回来的数据
2、然后用contentResolver和uri创建一个数据流
3、用BitmapFactory的解析这个输入流
4、最后将解析过后的输入流设置到图片上去

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        when (requestCode) {
            // 相册
            IMAGE_CODE -> {
                if (resultCode != Activity.RESULT_CANCELED) {
                    val uri: Uri? = data?.data 
                    uri?.let {
                        contentResolver.openInputStream(uri).use {
                            BitmapFactory.decodeStream(it).apply {
                                // 显示图片
                                ykImage.setImageBitmap(this)
                                // 缓存图片: 创建保存图片的文件
                                var file = File(filesDir, "header.jpg")
                                FileOutputStream(file).also { yk2 ->
                                    // 将图片保存到对应的路径中
                                    compress(Bitmap.CompressFormat.JPEG, 50, yk2)
                                }
                            }
                        }
                    }
                }
            }
  • 从文件中获取图片:
        // 获取头像图片(下一次进入程序时,去获取图片)
        File(filesDir, "header.jpg").also {
            if (it.exists()) {
                BitmapFactory.decodeFile(it.path).also { yk ->
                    ykImage.setImageBitmap(yk)
                }
            }
        }
    }

11、变量的分类:

  • 按数据类型分类:基本数据类型、引用数据类型
  • 按照在类中声明的位置分:成员变量(类变量、实例变量)、局部变量(在使用之前必须显示初始化)

12、静态变量与局部变量的区别:

  • 类变量有两次初始化的机会,第一次是在“准备阶段”,执行系统初始化,对类变量设置零值,另一次则是在“初始化”阶段,赋予程序员在代码中定义的初始值。
  • 和类变量初始化不同的是,局部变量表不存在系统初始化的过程,这意味着一旦定义了局部变量则必须人为的初始化,否则无法使用。

14、static关键字:

  • 类加载阶段,static关键字就会被加载
  • 方便在没有创建对象的情况下来进行调用(方法/变量)
  • static可以用来修饰类的成员方法、类的成员变量
  • 可以编写static代码块来优化程序性能,因为它的特性:只会在类加载的时候执行一次
  • 静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响
  • Java中的static关键字不会影响到变量或者方法的作用域
  • static是不允许用来修饰局部变量。不要问为什么,这是Java语法的规定。

14、final关键字:

  • 修饰类:表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。
  • 修饰方法:把方法锁定,以防任何继承类修改它的含义
  • 修饰变量:一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
15、 String为什么不可变

相关文章

网友评论

      本文标题:Android面试知识点(一)*

      本文链接:https://www.haomeiwen.com/subject/dtpygktx.html