对象加载的流程
注意
类的成员变量在不同对象中各不相同,都有自己的存储空间,即存储在各自对象所占的堆内存空间中,而类的方法是该类的所有对象所共享,它们存放在方法区中,各个方法的引用存储在常量池中,方法区优先于对象存在而且对象只保存了成员变量和一些地址信息
举例:People p = new People(“XiaoMing”, 10)
1:JAVA源文件先被编译成class文件,再由Java虚拟机(JVM)将其加载到内存中
2:将类型信息(包括静态变量、方法等)加载到方法区
3:执行类中的static代码块
4:对People类进行初始化,在堆内存开辟空间,并未对象分配首地址
5:在堆内存堆成员属性进行初始化(先默认初始化再显示初始化)
6:再对对象进行构造代码初始化、构造函数初始化
7:将堆内存中的地址赋给栈内存的p变量
volatile 保证可见性、有序性,不保证原子性
可见性:每次读取都能读到最新的值,可是并不会更新已经读了的值,它也无法更新已经读了的值。
有序性:按代码书写顺序执行,不会重排序
不保证原子性:如果在修改之前已经读取了值,那么修改之后,无法更新已经读取的值
synchronized 保证原子性、可见性、有序性。用来修饰方法或者代码块
synchronized修饰类的静态方法或者用类的字节码对象则锁就是类锁
synchronized修饰对象的非静态方法或者用this则锁就是当前创建的对象
对象锁:
每个线程都new一个相同类的实例对象,调用相同的同步方法或者不同的同步方法或者普通方法,两个线程之间都互不影响
每个线程操作同一个实例对象
1:调用这个实例对象的两个不同的同步方法,则必须等到一个同步方法执行完才
能执行另一个同步方法
2:调用这个实例对象的一个同步方法和一个普通方法,则可同步执行
类锁:
每个线程都new一个相同类的实例对象
1:调用相同的静态同步方法或者不同的静态同步方法,则必须等到一个同步方法
执行完才能执行另一个同步方法
2:调用每个实例对象的一个同步静态方法和(一个非静态的同步方法或一个普通
方法),则可同步执行
每个线程操作同一个实例对象
1:调用相同的静态同步方法或者不同的静态同步方法,则必须等到一个同步方法
执行完才能执行另一个同步方法
2:调用这个实例对象的一个同步静态方法和(一个非静态的同步方法或一个普通
方法—),则可同步执行(说明可以同时获取类锁和对象锁)
数据加密介绍
对称加密:使用对称密码编码技术,也就是编码和解码采用相同描述字符,即加密和解密使用相同的密钥,实现这种加密技术的算法称为加密算法。
对称加密使用简单,密钥较短,加密和解密过程较快,耗时短
常见的对称价目算法有DES,3DES,IDEA,AES,RC4等
·
是否安全
不安全 假设有黑客截取了该通信,这意味着黑客拥有了密钥和密文(加密后的数据)
一旦黑客有了密钥,解密密文就是很简单的事情了,我们所发送的数据就出现了泄露
·
非对称加密:与对称加密不同,其加密算法需要两个密钥:公开密钥(public key)和私有密钥(private key),两者是一对的。如果用公钥加密,只能用私钥解密。
非对称加密保密性较好,但加密和解密花费的时间较长
不适合对大文件加密而只适合对少量的数据加密。常见的非对称加密算法有RSA,ECC,DSA(数字签名)等
·
是否安全
安全 即使黑客截取了该通信,但因为没有私钥也就无法解密密文
但是使用非对称加密比对称加密更加耗时。为了用户体验,不建议使用非对称加密这种方式加密/解密大文件数据
·
Hash算法:是一种单向算法,通过Hash算法可以对目标数据生成一段特定长度、唯一的hash值,但是不能通过这个hash值重新计算出原始的数据,因此也称之谓摘要算法,经常被用在不需要数据还原的密码加密以及数据完整性校验上。
常见的算法有MD2、MD4、MD5和SHA等
·
HTTPS工作的流程
·
- 服务端生成一对非对称密钥:公钥和私钥,我们称之为“public key”,“private key”
- 服务端将公钥(public key)发送到客户端
- 客户端生成一个对称密钥(key2)
- 使用客户端密钥(key2)加密需要传递的数据,得到加密数据(result1)
- 使用公钥(public key)加密客户端的密钥(key2),得到加密数据(result2)
- 将result1和result2传递给服务端
- 服务端使用私钥(private key)解密result2得到客户端密钥(key2)
- 服务端使用密钥(key2)解密数据result1,得到客户端传递来的数据
通过上述方式,数据被很安全的传递到了服务器端,同时也保证了传递效率
https综合利用了非对称加密的安全性和堆成加密的效率,达到了一种近乎完美的数据传递功能
TCP和UDP区别
TCP:传输控制协议 UDP:用户数据报协议
TCP:面向连接的 UDP:无连接的
TCP:提供可靠服务,通过TCP连接传送数据无差错、不丢失、不重复,UDP:只负责发送,不保证交付
TCP:要经过三次握手 UDP没有
TCP:有序发送、丢包自动重传 传输慢 UDP无序发送、丢包不重传 传输快
基于TCP的协议有:HTTP/HTTPS,Telnet,FTP,SMTP。
基于UDP的协议有:DHCP,DNS,SNMP,TFTP,BOOTP。
三次握手
主动打开连接的是客户端,被动打开连接是服务器
1:第一次握手
Client将标志位SYN置为1,随机产生一个值seq=j,并将数据包发送给Server,Client进入SYN_SENT同步已发送状态,等待Server确认
2:第二次握手
Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,确认号是ack = j+1(ACK和ack是两个值),随机产生一个值seq=K,并将数据包发送给Client确认连接请求,Server进入SYN_RCVD同步收到状态
3:第三次握手
Client收到确认后,还要向Server发送确认报文,报文为ACK=1,ack=K+1,自己序列号是seq=j+1,当Server收到Client的报文后,检查ACK=1,ack=K+1后,连接建立成功,此后双方就可以通信了。
四次挥手
1:第一次挥手
Client发送FIN=1释放报文,停止向Server发送数据,Client进入终止等待状态
2:第二次挥手
Server收到释放报文FIN=1后,发送确认报文,报文为ACK=1,ACK=序号+1,Client收到Server的确认请求后,进入终止等待2状态(还可以接收Server发送的数据),Server进入关闭等待状态
3:第三次挥手
Server将最后的数据发送完毕后,向Client发送释放报文FIN=1,并关闭Server到Client的数据传送,Server进入最后确认状态
4:第四次挥手
Client收到Server的释放报文,并发出确认后,Server收到确认就立即进入Close状态,而Client要等待2MSL才Close状态,Client等待2MSL目的:
1:保证客户端发送最后一次报文能到达服务器端,或者当发送报文后因各种原因没有到达服务器端时,服务器端会重新发送一次确认状态,让客户端再次发送确认报文
2:用于清除本次连接持续时间内所产生的所有报文都能从网络中消失,确保新的连接不会出现旧的连接报文
关闭连接为什么四次挥手
关闭连接时,服务器收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,而自己也未必全部数据都发送给对方了。
为什么反射影响效率呢
这里就需要提到一个东西,JIT编译器。JIT编译器它可以把字节码文件转换为机器码,这个是可以直接让处理器使用的,经过它处理的字节码效率提升非常大,但是它有一个缺点,就是把字节码转换成机器码的过程很慢,有的时候甚至还超过了不转换的代码效率(转换之后存在一个复用的问题,对于转换了的机器码,使用的次数越多就越值的)。因此,在JVM虚拟机中,也就产生了一个机制,把常用的、使用频率高的字节码通过JIT编译器转换,而频率低的就不管它。而反射的话则是直接越过了JIT编译器,不管是常用的还是非常用的字节码一律没有经过JIT编译器的转化,所以效率就会低。 而在Android里面,5.0之前使用的是Davlik虚拟机,它就是上面的机制,而在Android5.0之后Google使用了一个全新的ART虚拟机全面代替Davlik虚拟机。 ART虚拟机会在程序安装时直接把所有的字节码全部转化为机器码,虽然这样会导致安装时间边长,但是程序运行的效率提升非常大。
Parcelable 和Serializable 都可以用于Intent间的数据传递,那么如何选择了。
- Serializable是Java中的序列化接口,其使用起来简单但是开销大,序列化和反序列化过程都需要大量的 I/O操作。
- Parcelable是Android中的序列化方式,更适用于在Android平台上,它的缺点就是用起来稍微麻烦,但效率很高,这是Android推荐方式,因此,首选Parcelable。但通过Parcelable将对象序列化到存储设备中或将对象序列化后通过网络传输也都是可以的,但是这个过程会稍显复杂,因此这种情况下建议使用Serializable。
final修饰的变量赋值方法
- 直接赋值
- 在构造函数中赋值
- 在构造代码块中赋值
final static修饰的变量赋值方法
- 直接赋值
- 在静态代码块中赋值时
int和Integer有什么区别
int:基本数据类,Integer是int的包装类
int是直接存储数据值,integer是对象的引用,指向的是new的integer对象
int默认值是0,integer默认值是null
基本数据类型的包装类都是用final修饰的类,不能被继承
(1)通过new创建的两个integer 永远不相等
Integer i = new Integer(100);
Integer j = new Integer(100);
System.out.print(i == j); //false
(2)integer和int进行比较时,只要两个变量值相等,则永远是true(因为java会自动拆包装integer为int,然后进行比较,实际比较就是两个int的值)
Integer i = new Integer(100);
int j = 100;
System.out.println(i == j); //true
Integer i1 = new Integer(200);
int j1 = 200;
System.out.println(i1 == j1); //true
(3)非new生成的integer变量和new生成的integer对象比较,结果永远是false(因为非new生成的变量指向的是java常量池的对象,而new生成的对象指向的是堆中的对象,地址不同)
Integer i = new Integer(100);
Integer j = 100;
System.out.print(i == j); //false
(4)非new生成的integer对象,进行比较时,变量在-128—127之间,比较时true,否则false
Integer i = 100;
Integer j = 100;
System.out.print(i == j); //true
Integer i = 128;
Integer j = 128;
System.out.print(i == j); //false
LruCache介绍
首先创建LruCache对象,一般key为String,value为Bitmap如:LruCache(String
Bitmap),并传入缓存大小(大小计算公式为:系统分配给每个应用最大内存的八分之一,默认返回时字节),并重写sizeOf方法,在sizeOf方法中返回value值Bitmap的内存大小(注意:缓存的总容量和每个缓存对象的大小所用单位要一致)。然后开始往LruCache中put东西,每次put值都会调用sizeOf方法,把Bitmap的内存字节加到LruCache中变量size字段上,再通过trimToSize(size)方法,去比较已使用的缓存大小size和总大小maxSize,如何大于maxSize就清除近期最少使用的Bitmap,LruCache底层维护的是一个LinkedHashmap,近期使用的元素或新添加的元素放到链表的尾部,删除的时候删除头部。
判断两个类是否相等
1、两个类来自同一个Class文件
2、两个类是由同一个虚拟机加载
3、两个类是由同一个类加载器加载
类加载器虽然只用于实现类的加载动作,但是对于任意一个类,都需要由加载它的类加载器和这个类本身共同确立其在Java虚拟机中的唯一性。通俗的说,JVM中两个类是否“相等”,首先就必须是同一个类加载器加载的,否则,即使这两个类来源于同一个Class文件,被同一个虚拟机加载,只要类加载器不同,那么这两个类必定是不相等的。程序在启动的时候,并不会一次性加载所有的class文件,而是根据程序需要,通过java的类加载机制(ClassLoader)来动态加载某个class文件到内存当中,从而只有class文件被加载到内存后,才能被其他class所引用,所以类加载器就是用来动态加载class文件到内存当中用的。