一、线程中sleep和wait的区别
1,这两个方法来自不同的类分别是Thread和Object
2,最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法
3,wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
二、Thread中的start()和run()方法有什么区别
start()方法:启动一个线程,调用该Runnable对象的run()方法,不能多次启动一个线程
run()方法:在本线程内(常为main线程)调用该Runnable对象的run()方法,可以重复多次调用,也就是说,直接调用run()方法和调用普通方法一样(并不能启动一个新的线程,仍然按顺序执行,同步执行)
三、关键字final和static是怎么使用的
final类不能继承,没有子类,final类中的方法默认是final的
final方法不能被子类方法覆盖,但可以被继承
final成员变量表示常量,只能被赋值一次,赋值后值不再改变
final不能用于修饰构造方法
static表示“全局”或者“静态”的意思,用来修饰成员变量和成员方法,也可以形成静态static代码块,被static修饰的成员变量和成员方法独立于该类的任何对象
用public修饰的static成员变量和成员方法本质是全局变量和全局方法,当声明它类的对象时,不生成static变量的副本,而是类的所有实例共享同一个static变量
static final用来修饰成员变量和成员方法,可以理解为“全局变量”
四、String,StringBuffer,StringBuilder区别
String是Java中的类,在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串。String的值是不可变的,这就导致每次对String的操作都会生成新的String对象
当对字符串进行修改的时候,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。StringBuffer提供append和add方法,可以将字符串添加到已有序列的末尾或指定位置,它的本质是一个线程安全的可修改的字符序列,把所有修改数据的方法都加上了synchronized。但是保证了线程安全是需要性能的代价的
StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)
一般在多线程中操作字符串缓冲区下的大量数据使用StringBuffer;单线程操中操作字符串缓冲区下的大量数据使用StringBuilder
五、Java中重载和重写的区别
重载就是在同一类中允许同时存在一个以上的同名方法,但方法的参数类型不同
重写(重置、覆盖)是子类重新定义父类中己经定义的方法,即子类重写父类方法
六、equals跟==的区别
==是一个比较运算符,基本数据类型比较的是值,引用数据类型比较的是地址值(比较地址值即是指是否为同一个对象的引用)
equals()是Object基类一个方法,只能比较引用数据类型。重写前比较的是地址值,重写后比一般是比较对象的属性,如:String类中的equals()方法是被重写过的,比较的是对象属性
七、抽象类跟接口的区别
接口是对动作的抽象,抽象类是对根源的抽象。抽象类表示的是,这个对象是什么。接口表示的是,这个对象能做什么
所以,在高级语言上,一个类只能继承一个类(抽象类),但是可以实现多个接口(吃饭接口、走路接口)
接口是抽象类的变体,接口中所有的方法都是抽象的。而抽象类是声明方法的存在而不去实现它的类
接口定义方法,不能实现,而抽象类可以实现部分方法
接口中基本数据类型为static 而抽类象不是的
当你关注一个事物的本质的时候,用抽象类;当你关注一个操作的时候,用接口
八、抽象类能否实例化,为什么
不能
抽象类是一个不完整的类实际上抽象类更多提供的是一个框架的功能,参数比接口更详细些。因为在设计时,考虑到某些类所具备的信息不足以实例化一个对象,才设计成抽象的
内存分配问题:抽象类只分配了在栈中的引用,没有分配堆中的内存。程序都有一个代码段,再内存中需要占据一定的内存,而抽象类没有具体的实现方法,无法具体的给它分配内存空间,所以为了安全,不JAVA不允许抽象类,接口直接实例化
九、Java中的锁
Java多线程加锁的方式:
1,synchronized关键字
2,Java.util.concurrent包中的lock接口和ReentrantLock实现类
采用synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用;而Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象
Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现
Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;(I/O和Synchronized都能相应中断,即不需要处理interruptionException异常)
十、Http https区别,https的实现原理
HTTP:是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),用于从WWW服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少
HTTPS:是以安全为目标的HTTP通道,简单讲是HTTP的安全版,即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL
HTTPS协议的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种就是确认网站的真实性。具体说就是,一是建立SSL连接(信息加密连接),二是CA证书认证
十一、Http位于TCP/IP模型中的第几层?为什么说Http是可靠的数据传输协议?
从下往上第4层传输层(按5层分),整个5层为:5,应用层(HTTP协议,支持电子邮件的SMTP协议,支持文件传送的FTP协议,DNS,POP3,SNMP,Telnet等等),4,传输层(传输控制协议TCP、用户数据包协议UDP),3,网络层(IP,ICMP,IGMP,ARP,RARP),2,链路层(在两个相邻结点之间传送数据时,数据链路层将网络层交下来的IP数据报组装成帧(framing),在两个相邻结点之间的链路上“透明”地传送帧中的数据。每一帧包括数据和必要的控制信息(如同步信息、地址信息、差错控制等)),1,物理层(在物理层上所传数据的单位是比特。物理层的任务就是透明地传送比特流)
由于Http是在传输层基于TCP协议的,而TCP又是面向连接的可靠协议,所以Http是可靠的传输协议
十二、HTTP链接的特点
1,支持客户/服务器模式
2,简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快
3,灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记
4,无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间
5,无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快
十三、TCP和UDP的区别
1,连接方面区别:
TCP面向连接(如打电话要先拨号建立连接)
UDP是无连接的,即发送数据之前不需要建立连接
2,安全方面的区别:
TCP提供可靠的服务,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达
UDP尽最大努力交付,即不保证可靠交付
3,传输效率的区别
TCP传输效率相对较低
UDP传输效率高,适用于对高速传输和实时性有较高的通信或广播通信
4,连接对象数量的区别
TCP连接只能是点到点、一对一的
UDP支持一对一,一对多,多对一和多对多的交互通信
十四、Socket建立网络连接的步骤
建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket ,另一个运行于服务器端,称为ServerSocket 。套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认
十五、Tcp/IP三次握手,四次挥手
三次握手:
四次挥手:
十六、java注解,反射,泛型的理解与作用
注解:注解就类似一个标签,给字段,方法,类,或者给注解本身,做一个标记
javac编译工具、开发工具以及其他程序可以利用反射来了解你的类以及各种元素上有没有何种标记,就可以去做相应的事
标记可以加在包、类型(Type,包括类,枚举,接口)、字段、方法、方法的参数以及局部变量上面
反射:就是反过来获取类的信息。以前都是java文件最后生成class文件,现在反过来,在有Class对象的前提下,获取整个类的信息
泛型 :Java采用泛型擦除机制来引入泛型。Java中的泛型仅仅是给编译器Javac使用的,确保数据的安全性和免去强制类型转换的麻烦。在不知道要操作什么数据类型的时候,用它代替。像装数据的容器数组,集合等,或者即将操作的对象,都不清楚时,用它代替
十七、java多个线程如何同时请求,返回的结果如何等待所有线程数据完成后合成一个数据线程种类
同时请求:
1, 创建n个线程,加一个闭锁CountDownLatch,开启所有线程,待所有线程都准备好后,按下开启按钮,就可以真正的发起并发请求了
2,使用CyclicBarrier
等待所有线程数据完成后合成一个数据线程种类:
1,放置一个公用的static变量,每个线程处理完上去累加下结果,然后后面用一个死循环,去数这个结果
2, 使用CountDownLatch
3,借助FutureTask,达到类似的效果,其get方法会阻塞线程,等到该异步处理完成。缺点就是,FutureTask调用的是Callable,必须要有返回值
4,使用Thread的Join方法
5,使用同步屏障CyclicBarrier
十八、JAVA GC原理
如何判断一个对象已经死去:可达性分析
GC算法:标记-清除(Mark-Sweep)算法 复制算法 标记-整理(Mark-Compat)算法 分代收集(Generational Collection)算法
内存分配
对象优先在Eden分配
大对象直接进老年代
长期存活的对象将进入老年代
十九、volatile的作用和原理
1,保持内存可见性
volatile如何保持内存可见性
Java通过几种原子操作完成工作内存和主内存的交互:
1, lock:作用于主内存,把变量标识为线程独占状态
2, unlock:作用于主内存,解除独占状态
3,read:作用主内存,把一个变量的值从主内存传输到线程的工作内存
4,load:作用于工作内存,把read操作传过来的变量值放入工作内存的变量副本中
5, use:作用工作内存,把工作内存当中的一个变量值传给执行引擎
6, assign:作用工作内存,把一个从执行引擎接收到的值赋值给工作内存的变量
7, store:作用于工作内存的变量,把工作内存的一个变量的值传送到主内存中
8,write:作用于主内存的变量,把store操作传来的变量的值放入主内存的变量中
volatile的特殊规则就是:
read、load、use动作必须连续出现
assign、store、write动作必须连续出现
所以,使用volatile变量能够保证:
每次读取前必须先从主内存刷新最新的值
每次写入后必须立即同步回主内存当中
也就是说,volatile关键字修饰的变量看到的随时是自己的最新值。线程1中对变量v的最新修改,对线程2是可见的
2,防止指令重排
下面是基于保守策略的JMM内存屏障插入策略:
在每个volatile写操作的前面插入一个StoreStore屏障
在每个volatile写操作的后面插入一个StoreLoad屏障
在每个volatile读操作的后面插入一个LoadLoad屏障
在每个volatile读操作的后面插入一个LoadStore屏障
二十、ReentrantLock原理
ReentrantLock主要利用CAS+CLH队列来实现。它支持公平锁和非公平锁,两者的实现类似
CAS:Compare and Swap,比较并交换。CAS有3个操作数:内存值V、预期值A、要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。该操作是一个原子操作,被广泛的应用在Java的底层实现中。在Java中,CAS主要是由sun.misc.Unsafe这个类通过JNI调用CPU底层指令实现
CLH队列:带头结点的双向非循环链表
ReentrantLock的基本实现可以概括为:先通过CAS尝试获取锁。如果此时已经有线程占据了锁,那就加入CLH队列并且被挂起。当锁被释放之后,排在CLH队列队首的线程会被唤醒,然后CAS再次尝试获取锁。在这个时候,如果:
非公平锁:如果同时还有另一个线程进来尝试获取,那么有可能会让这个线程抢先获取
公平锁:如果同时还有另一个线程进来尝试获取,当它发现自己不是在队首的话,就会排到队尾,由队首的线程获取到锁
二十一、Java为什么要推出HashMap,它是如何解决hash冲突的
常规的线性存储,你若需要找到其中的某个元素,就需要遍历这个链表或者数组,而遍历的同时需要让链表中的每一个元素都和目标元素做比较,相等才返回,Java里面用equals或者==。这对性能是毁灭性的伤害
HashMap的优势:Hash算法就是根据某个算法将一系列目标对象转换成地址,当要获取某个元素的时候,只需要将目标对象做相应的运算获得地址,直接获取
如果存在相同的hashcode,那么他们确定的索引位置就相同,这时判断他们的key是否相同,如果不相同,这时就是产生了hash冲突。 Hash冲突后,那么HashMap的单个bucket里存储的不是一个 Entry,而是一个 Entry 链。系统只能必须按顺序遍历每个 Entry,直到找到想搜索的 Entry 为止——如果恰好要搜索的 Entry 位于该 Entry 链的最末端(该 Entry 是最早放入该 bucket 中),那系统必须循环到最后才能找到该元素。
二十二、ArrayMap跟SparseArray在HashMap上面的改进
HashMap中默认的存储大小就是一个容量为16的数组,所以当我们创建出一个HashMap对象时,即使里面没有任何元素,也要分别一块内存空间给它,而且,我们再不断的向HashMap里put数据时,当达到一定的容量限制时(这个容量满足这样的一个关系时候将会扩容:HashMap中的数据量>容量*加载因子,而HashMap中默认的加载因子是0.75),HashMap的空间将会扩大,而且扩大后新的空间一定是原来的2倍。假如我们有几十万、几百万条数据,那么HashMap要存储完这些数据将要不断的扩容,而且在此过程中也需要不断的做hash运算,这将对我们的内存空间造成很大消耗和浪费,而且HashMap获取数据是通过遍历Entry[]数组来得到对应的元素,在数据量很大时候会比较慢,所以在Android中,HashMap是比较费内存的,我们在一些情况下可以使用SparseArray和ArrayMap来代替HashMap
SparseArray比HashMap更省内存,在某些条件下性能更好,主要是因为它避免了对key的自动装箱(int转为Integer类型),它内部则是通过两个数组来进行数据存储的,一个存储key,另外一个存储value,为了优化性能,它内部对数据还采取了压缩的方式来表示稀疏数组的数据,从而节约内存空间。SparseArray只能存储key为int类型的数据,同时,SparseArray在存储和读取数据时候,使用的是二分查找法。SparseArray存储的元素都是按元素的key值从小到大排列好的,在获取数据的时候非常快,比HashMap快的多
虽说SparseArray性能比较好,但是由于其添加、查找、删除数据都需要先进行一次二分查找,所以在数据量大的情况下性能并不明显,将降低至少50%
满足下面两个条件我们可以使用SparseArray代替HashMap:
数据量不大,最好在千级以内
key必须为int类型,这中情况下的HashMap可以用SparseArray代替
ArrayMap是一个
SparseArray和ArrayMap都差不多,使用哪个呢?
假设数据量都在千级以内的情况下:
1,如果key的类型已经确定为int类型,那么使用SparseArray,因为它避免了自动装箱的过程,如果key为long类型,它还提供了一个LongSparseArray来确保key为long类型时的使用
2,如果key类型为其它的类型,则使用ArrayMap
二十三、java虚拟机和Dalvik虚拟机的区别
1,java虚拟机(JVM)基于栈。 基于栈的机器必须使用指令来载入和操作栈上数据,由于手机上的硬件资源有限,无法支撑JVM频繁的从栈上读写的开销,dalvik虚拟机是基于寄存器的,其数据的访问通过寄存器间直接传递,效率远高于栈
2,java虚拟机运行的是java字节码。(java类会被编译成一个或多个字节码.class文件,打包到.jar文件中,java虚拟机从相应的.class文件和.jar文件中获取相应的字节码)
Dalvik运行的是自定义的.dex字节码格式。(java类被编译成.class文件后,会通过一个dx工具将所有的.class文件转换成一个.dex文件,然后dalvik虚拟机会从其中读取指令和数据)
Dalvik的Dex格式在未压缩的情况下都比压缩了的Jar文件还小
3,常量池已被修改为只使用32位的索引,以 简化解释器。dalvik的堆和栈的参数可以通过-Xms和-Xmx更改
4,一个应用,一个虚拟机实例,一个进程(所有android应用的线程都是对应一个linux线程,都运行在自己的沙盒中,不同的应用在不同的进程中运行。每个android dalvik应用程序都被赋予了一个独立的linux PID(app_*))
整体上可以把Dalvik理解为为手机设备优化的jvm
二十四、设计模式、手写单例
java的设计模式大体上分为三大类:
创建型模式(5种):工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式
结构型模式(7种):适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式
行为型模式(11种):策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式
设计模式遵循的原则有6个:
1,开闭原则(Open Close Principle)
对扩展开放,对修改关闭。
2,里氏代换原则(Liskov Substitution Principle)
只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为
3,依赖倒转原则(Dependence Inversion Principle)
这个是开闭原则的基础,对接口编程,依赖于抽象而不依赖于具体
4,接口隔离原则(Interface Segregation Principle)
使用多个隔离的借口来降低耦合度
5,迪米特法则(最少知道原则)(Demeter Principle)
一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立
6,合成复用原则(Composite Reuse Principle)
原则是尽量使用合成/聚合的方式,而不是使用继承。继承实际上破坏了类的封装性,超类的方法可能会被子类修改
静态内部类单例:
public class SingletonDemo {
private static class SingletonInstance{
private static final SingletonDem instance=new SingletonDemo();
}
private SingletonDemo(){}
public static SingletonDemo getInstance(){
return SingletonInstance.instance;
}
}