Java教程

Android 和 Java 学习笔记

本文主要是介绍Android 和 Java 学习笔记,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

App应用层 —(发起网络请求)—> Retrofit ——> OkHttp —(返回响应数据)—> 服务器 


OkHttp:

1.创建  OkHttpClient 实例
2.创建  Request 
3.OkHttpClient  生成一个  Call  对象实例  (RealCall)
4.发送请求 (同步、异步)

val okHttpClient = new OkHttpClient().Builder().build();
val request = Request.Builder().build().url("http://www.baidu.com").get().build();
val newCall = okHttpClient.newCall(request);
val response = newCall.execute();


ArrayList,LinkList线程不安全   ArrayList底层是可变数组非同步实现的  LinkList底层是基于双向链表非同步实现的

ArrayList在插入删除时,会重新排列位置,速度比较慢   查询是根据角标查询数据的比较快

LinkList在增删时位置不变,效率较高   查询时需要遍历节点,查询比较慢

Vectory与ArrayList类似  加了同步对象锁机制,线程安全

如果程序要求多线程下操作则选择Vectory,如果不要求线程安全且查询速度快的情况下则选择使用ArrayList,如果插入删除需求较多的情况下则选择LinkedList。

set 与 list 的区别:set 唯一无序,List 不唯一有序  


问:Java中String、StringBuilder、StringBuffer有什么区别?

String是final修饰,即终态数据类型,变量的值不可改变,在String字符串拼接时会产生大量的临时变量。如果有大量拼接操作时,使用StringBuilder或StringBuffer,前者线程不安全效率高,后者线程安全效率低


线程同步有哪些方式:
答:使用多线程时,保证数据的唯一性和准确性,所以要使用线程同步,线程同步有以下五种方式
1.synchronized 关键字(对方法或代码块)
2.使用重入锁 ReentrantLock(), 需要手动lock()和unlock();
3.使用局部变量ThreadLocal,
4.使用阻塞队列 LinkedBlockingQueue<E e>,put(E e)队列末尾添加元素,如果队列满则阻塞,take()返回队首并移除,如果队空则阻塞
5.使用特殊域变量(volatile)实现线程同步


(三)问:什么是死锁?如何解决或预防产生死锁
答:死锁,就是指两个或是两个以上的线程/进程在执行的过程中,因争夺资源而造成的一种相互等待的现象。

解决方案:
1.避免在同步代码块中调用外部的同步方法
2.在嵌套多层 synchronized 代码块中,通过对象唯一值给对象锁进行排序,例如使用对象的hashCode值进行排序,使用每次获取锁的顺序是一致的

问:什么是泛型,泛型擦除是什么,泛型的作用
泛型就是参数化类型,适用于多种数据类型执行相同的代码,在使用时才确定真实类型。泛型有泛型类、泛型接口、泛型方法。
泛型擦除:泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉,这叫做泛型擦除

问:什么是反射,反射的使用场景
使用反射,能在运行状态时,对于任意一个类,都能够知道这个类的所有属性和方法、类信息;对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态能力称为反射机制

类加载过程是 new 一个类时,JVM会加载相应xxx.class到内存中并创建class对象。反射是获取class对象反向获取类信息的过程。

问:Java中四种引用类型
答:四种引用类型分别为 强引用、弱引用、软引用、虚引用

强引用(StrongReference):强引用是最为普遍和使用最多的一种引用方式,如果一个对象具有强引用,当内存不足时,虚拟机宁愿抛出OOM异常结束应用,也不会去回收这个对象。例如:A a = new A() 即a对象具有强引用。
弱引用(WeakReference):只具有弱引用的对象在垃圾回收线程扫描其所管辖的内存区域时,不管当前内存是否不足都会直接回收它的内存。弱引用可以和引用队列(ReferenceQueue)结合使用,弱引用对象内存回收前,会将当前引用加入到引用队列中
软引用(SoftReference):只具有软引用的对象在垃圾回收线程扫描其所管些的内存区域时,会判断当前内存是否不足,如果当前内存不足则回收软引用对象的内存,如果当前内存足够,则不回收。软引用可以和引用队列(ReferenceQueue)结合使用,软引用对象内存回收前,会将当前引用加入到引用队列中
虚引用(PhantomReference):如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动,必须配合引用队列(ReferenceQueue)联合使用,虚引用对象内存回收前,会将当前引用加入到引用队列中。
问:Handler、AsyncTask、Thread之类的内存泄漏优化为何选弱引用而不用软引用
答:以Handler为例,Handler发生内存泄漏是因为非静态内部类持有外部类Activity的引用,Activity退出时,长生命周期的Handler任然可能持有对Activity的引用,导致Activity无法被回收,导致内存泄漏。在activity线程的GC过程中遇到软引用不一定会对其进行回收,只有内存紧张的情况才开始回收,并且是优先回收较久远的activity,所以如果使用弱引用也可能会引起内存泄漏。


模块化与组件化
模块化
模块化就是将一个程序按照其功能做拆分,分成相互独立的模块,以便于每个模块只包含与其功能相关的内容,模块我们相对熟悉,比如登录功能可以是一个模块,
搜索功能可以是一个模块等等。

组件化
组件化就是更关注可复用性,更注重关注点分离,如果从集合角度来看的话,可以说往往一个模块包含了一个或多个组件,或者说模块是一个容器,由组件组装而成。
简单来说,组件化相比模块化粒度更小,两者的本质思想都是一致的,都是把大往小的方向拆分,都是为了复用和解耦,只不过模块化更加侧重于业务功能的划分,
偏向于复用,组件化更加侧重于单一功能的内聚,偏向于解耦。

Rxjava的操作符有哪些,说说他们的作用
just:将同种数据源组合放到被观察者上面
from:将类似数组、集合的数据源放到被观察者上面
map:将一种数据源,转化成另外一种
flatmap:将一种数据源,转化成另外一种数据,并且被转化的数据是乱序排列的
concatmap:将一种数据源,转化成另外一种数据,并且被转化的数据是按照先前的数据源顺序排序的
toList:将数组的形式转化成List集合
subscribeOn:设置Observable的call方法所在的线程,也就是数据来源的线程
observeOn:设置subscribe的call方法所在的线程,也就是数据处理的线程
filter:在被观察者的数据层过滤数据
one rrorResumeNext:出错的时候,可以指定出错的时候的被观察者
retryWhen:出错的时候,重新走一遍被订阅的过程
concat:合并相同类型的被观察者到一个被观察者身上,有点类似集合、数组拼接数据。
zip:处理多种不同结果集的数据发射,一般用得多的地方是多个网络请求组合然后统一处理业务逻辑。
还有很多操作符就自己去看,这些操作符已经够面试用的了。


启动流程:
Android系统启动流程的主要经历init进程 ——> Zygote进程 ——> SystemServer进程 ——> 各种系统服务 ——> 应用进程等阶段


进程:进程是并发执行程序在执行过程中资源分配和管理的基本单位      进程拥有自己的独立地址空间
线程:程序执行的最小单位


List接口存储一组数据不唯一,有序(插入顺序)的对象
Set接口存储一组数据唯一,无序的对象


HashMap(数组、链表、红黑树(JDK1.8 加的))   ->   hashing原理   put()或get()方法储存和获取对象

调用put()方法时,它调用键的对象hashCode()方法来计算hashCode,让后找到bucket位置来储存Entry对象

当两个对象的hashCode相同时,它们的bucket位置相同,会发生碰撞

HashMap使用链表存储对象,这个Entry会存储在链表中,当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象


HashMap是非synchronized(同步)的,性能更好,HashMap可以接受为null的key-value

HashTable是线程安全的,比HashMap要慢,不接受null的key-value


ConcurrentHashMap ->  Segment  ->   HashMap

Segment<K,V> s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments,u)


LinkedList 本质上是一个双向链表的存储结构:

元素查询:ArrayList由于LinkedList   因为LinkedList要移动指针

元素新增和删除操作:LinkedList比较占优势,因为ArrayList要移动数据

Java的线程⽣命周期有六种状态:
1 New(初始化状态)
2 Runnable(可运⾏/运⾏状态)
3 Blocked(阻塞状态)
4 Waiting(⽆时间限制的等待状态)
5 Timed_Waiting(有时间限制的等待状态)
6 Terminated(终⽌状态)


线程死锁的四个条件:
1 互斥条件
2 请求并持有条件
3 不可剥夺条件
4 环路等待条件


(1)HashSet 内部使用 HashMap 的 key 存储元素,以此来保证元素不重复;
(2)HashSet 是无序的,因为 HashMap 的 key 是无序的;
(3)HashSet 中允许有一个 null 元素,因为 HashMap 允许 key 为 null;
(4)HashSet 是非线程安全的;
(5)HashSet 是没有 get()方法的;

方法指令:
1.invokeinterface:用于调用接口方法
2.invokevirtual:指令用于调用对象的实例方法
3.invokestatic:用于调用类/静态方法
4.invokespecial:用于调用一些需要特殊处理的实例方法,包括实例初始化方法、私有方法和父类方法


类加载器:
1.BootstrapClassLoader
2.ExtClassLoader
3.AppClassLoader


工作原理:
1.委托机制:加载任务委托给父类加载器,如果不行就向下传递委托任务,由其子类加载器加载,保证Java核心库的安全性
2.可见性机制:子类加载器可以看到父类的加载器加载的类,而反之则不行
3.单一性机制:父加载器加载过得类不能被子类加载器加载第二次


Object:
1.equals:对两个对象的地址值进行的比较(即比较引用是否相同)

2.hashcode:hashCode()方法给对象返回一个hash code 值。这个方法被用于hash tables,例如HashMap

3.static:
(1)static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问。
(2)静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。
(3)能通过 this 访问静态成员变量吗? 所有的静态方法和静态变量都可以通过对象访问(只要访问权限足够)。
(4)static是不允许用来修饰局部变量

4.final:
(1)可以声明成员变量、方法、类以及本地变量
(2)final 成员变量必须在声明的时候初始化或者在构造器中初始化,否则就会报编译错误
(3)final 变量是只读的
(4)final 申明的方法不可以被子类的方法重写
(5)final 类通常功能是完整的,不能被继承
(6)final 变量可以安全的在多线程环境下进行共享,而不需要额外的同步开销
(7)final 关键字提高了性能,JVM 和 Java 应用都会缓存 final 变量,会对方法、变量及类进行优化
(8)方法的内部类访问方法中的局部变量,但必须用 final 修饰才能访问


String、StringBuffer、StringBuilder
(1)String 是 final 类,不能被继承。对于已经存在的 Stirng 对象,修改它的值,就是重新创建一个对象
(2)StringBuffer 是一个类似于 String 的字符串缓冲区,使用 append() 方法修改 Stringbuffer 的值,使用 toString() 方法转换为字符串,是线程安全的
(3)StringBuilder 用来替代于 StringBuffer,StringBuilder 是非线程安全的,速度更快

内部类
(1)非静态内部类没法在外部类的静态方法中实例化。
(2)非静态内部类的方法可以直接访问外部类的所有数据,包括私有的数据。
(3)在静态内部类中调用外部类成员,成员也要求用 static 修饰。
(4)创建静态内部类的对象可以直接通过外部类调用静态内部类的构造器。
(5)创建非静态的内部类的对象必须先创建外部类的对象,通过外部类的对象调用内部类的构造器。


匿名内部类
(1)匿名内部类不能定义任何静态成员、方法
(2)匿名内部类中的方法不能是抽象的
(3)匿名内部类必须实现接口或抽象父类的所有抽象方法
(4)匿名内部类不能定义构造器
(5)匿名内部类访问的外部类成员变量或成员方法必须用 final 修饰


抽象和接口
(1)抽象类不能有对象(不能用 new 关键字来创建抽象类的对象)
(2)抽象类中的抽象方法必须在子类中被重写
(3)接口中的所有属性默认为:public static final ****;
(4)接口中的所有方法默认为:public abstract ****;


集合:
List接口存储一组不唯一,有序(插入顺序)的对象, Set接口存储一组唯一,无序的对象。

HashMap 与 HashTable 对比
HashMap 是非 synchronized 的,性能更好,HashMap 可以接受为 null 的 key-value,而 Hashtable 是线程安全的,比 HashMap 要慢,不接受 null 的 key-value。


对于元素查询来说,ArrayList 优于 LinkedList,因为 LinkedList 要移动指针。对于新增和删除操作,LinedList 比较占优势,因为 ArrayList 要移动数据。

volatile
当把变量声明为 volatile 类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。
volatile 变量不会被缓存在寄存器或者对其他处理器不可见的地方,JVM 保证了每次读变量都从内存中读,跳过 CPU cache 这一步,因此在读取 volatile 类型的变量时总会返回最新写入的值。

当一个变量定义为 volatile 之后,将具备以下特性:
(0)保证此变量对所有的线程的可见性,不能保证它具有原子性(可见性,是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的)
(2)禁止指令重排序优化
(2)volatile 的读性能消耗与普通变量几乎相同,但是写操作稍慢,因为它需要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行
(3)AtomicInteger 中主要实现了整型的原子操作,防止并发情况下出现异常结果,其内部主要依靠 JDK 中的 unsafe 类操作内存中的数据来实现的。
(4)volatile 修饰符保证了 value 在内存中其他线程可以看到其值得改变。
(5)CAS(Compare and Swap)操作保证了 AtomicInteger 可以安全的修改value 的值。


锁的分类:
1.线程要不要锁住同步资源:
(1):锁住——>悲观锁(synchronized 关键字和 Lock 的实现类都是悲观锁。悲观锁适合写操作多的场景,先加锁可以保证写操作时数据正确)
(2):不锁住——>乐观锁( CAS 算法,Java 原子类中的递增操作就通过 CAS 自旋实现。乐观锁适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅提升)
2.多个线程竞争锁时要不要排队:
(1):排队——>公平锁
(2):不排队——>非公平锁
3.多个线程能不能共享一把锁:
(1):能——>共享锁
(2):不能——>排他锁

死锁
当前线程拥有其他线程需要的资源,当前线程等待其他线程已拥有的资源,都不放弃自己拥有的资源。

View 的整个绘制流程可以分为以下三个阶段:

1.measure: 判断是否需要重新计算 View 的大小,需要的话则计算
2.layout: 判断是否需要重新计算 View 的位置,需要的话则计算
3.draw: 判断是否需要重新绘制 View,需要的话则重绘制


IPC(进程间的通信):
1.Bundle  只能传输Bundle支持的数据类型      四大组件间的进程间的通信
2.文件共享    不适合高并发场景,并且无法做到进程间的即时通信    
3.AIDL  
4.Messenger
5.ContentProvider  
6.Socket

apply / commit
apply 没有返回值, commit 有返回值能知道修改是否提交成功
apply 是将修改提交到内存,再异步提交到磁盘文件,而 commit 是同步的提交到磁盘文件
多并发的提交 commit 时,需等待正在处理的 commit 数据更新到磁盘文件后才会继续往下执行,从而降低效率; 
而 apply 只是原子更新到内存,后调用 apply 函数会直接覆盖前面内存数据,从一定程度上提高很多效率。

计算机网络基础:

网络体系的分层结构:
1.物理层:
2.数据链路层
3.网络层
4.运输层
5.应用层
 

这篇关于Android 和 Java 学习笔记的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!