乱七八糟
1.JDK 和 JRE 有什么区别?
JDK:Java Development Kit 的简称,java 开发工具包,提供了 java 的开发环境和运行环境。
JRE:Java Runtime Environment 的简称,java 运行环境,为 java 的运行提供了所需环境。
具体来说 JDK 其实包含了 JRE,同时还包含了编译 java 源码的编译器 javac,还包含了很多 java 程序调试和分析的工具。简单来说:如果你需要运行 java 程序,只需安装 JRE 就可以了,如果你需要编写 java 程序,需要安装 JDK。
2.两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?
解答:
不对,两个对象的 hashCode()相同,equals()不一定 true。(两本书有着同样的书名和作者,但是它们是两本“不同”的书。)
代码解读:很显然“通话”和“重地”的 hashCode() 相同,然而 equals() 则为 false,因为在散列表中,hashCode()相等即两个键值对的哈希值相等,然而哈希值相等,并不一定能得出键值对相等。
equals
equals()方法是用来判断其他的对象是否和该对象相等,它的性质有:
自反性(reflexive)。对于任意不为null的引用值x,x.equals(x)一定是true。
对称性(symmetric)。对于任意不为null的引用值x和y,当且仅当x.equals(y)是true时,y.equals(x)也是true。
传递性(transitive)。对于任意不为null的引用值x、y和z,如果x.equals(y)是true,同时y.equals(z)是true,那么x.equals(z)一定是true。
一致性(consistent)。对于任意不为null的引用值x和y,如果用于equals比较的对象信息没有被修改的话,多次调用时x.equals(y)要么一致地返回true要么一致地返回false。
对于任意不为null的引用值x,x.equals(null)返回false。
hashCode
hashCode()方法给对象返回一个hash code值。这个方法被用于HashTable,HashMap。
它的性质是:
在一个Java应用的执行期间,如果一个对象提供给equals做比较的信息没有被修改的话,该对象多次调用hashCode()方法,该方法必须始终如一返回同一个integer。
如果两个对象根据equals(Object)方法是相等的,那么调用二者各自的hashCode()方法必须产生同一个integer结果。
并不要求根据equals(java.lang.Object)方法不相等的两个对象,调用二者各自的hashCode()方法必须产生不同的integer结果。然而,程序员应该意识到对于不同的对象产生不同的integer结果,有可能会提高hash table的性能。
大量的实践表明,由Object类定义的hashCode()方法对于不同的对象返回不同的integer。
在每个改写了equals()方法的类中,必须要改写hashCode()方法。如果不这样做,就会违反Object.hashCode的通用约定,从而导致该类无法与所有基于hash的集合类结合在一起正常运作,这样的集合类包括HashMap、HashSet和HashTable。
需要注意的是: 这个hashCode()方法是合法的,因为相等的对象总是具有同样的散列码.
1 | public int hashCode() { return 0; } |
但是它使得每一个对象都具有同样的散列码。因此,每个对象都被映射到同一个散列桶中,从而散列表被退化为链表。对于规模很大的散列表而言,这关系到散列表能否正常工作。
一个好的散列函数通常倾向于“7为不相等的对象产生不相等的散列码”。
如果一个类是非可变的,并且计算散列码的代价也比较大,那么你应该考虑把散列码缓存在对象内部,而不是每次请求的时候都重新计算散列码。
不要试图从散列码计算中排除掉一个对象的关键部分以提高性能
3.final 在 java 中有什么作用?
final 修饰的类叫最终类,该类不能被继承。
final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。
final 修饰的方法不能被重写。
4.String str=”i”与 String str=new String(“i”)一样吗?
不一样,因为内存的分配方式不一样。String str=”i”的方式,(JVM java va)java 虚拟机会将其分配到常量池->方法区中;
而 String str=new String(“i”) 则会被分到堆内存中。
5.如何将字符串反转?
使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。
6.String 类的常用方法都有那些?
indexOf():返回指定字符的索引。
charAt():返回指定索引处的字符。
replace():字符串替换。
trim():去除字符串两端空白。
split():分割字符串,返回一个分割后的字符串数组。
getBytes():返回字符串的 byte 类型数组。
length():返回字符串长度。
toLowerCase():将字符串转成小写字母。
toUpperCase():将字符串转成大写字符。
substring():截取字符串。
equals():字符串比较。
7.抽象类必须要有抽象方法吗?
不需要,抽象类不一定非要有抽象方法。
在《JAVA编程思想》一书中,将抽象类定义为“包含抽象方法的类”,但是后面发现如果一个类不包含抽象方法,只是用abstract修饰的话也是抽象类。也就是说抽象类不一定必须含有抽象方法。个人觉得这个属于钻牛角尖的问题吧,因为如果一个抽象类不包含任何抽象方法,为何还要设计为抽象类?所以暂且记住这个概念吧
8.普通类和抽象类有哪些区别?
抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public。
抽象类不能用来创建对象;
如果一个类继承于一个抽象类,则子类必须实现父类的抽象方法。如果子类没有实现父类的抽象方法,则必须将子类也定义为为abstract类。
在其他方面,抽象类和普通的类并没有区别。
普通类不能包含抽象方法,抽象类可以包含抽象方法。
抽象类不能直接实例化,普通类可以直接实例化。
9.java 中 IO 流分为几种?
按功能来分:输入流(input)、输出流(output)。
按类型来分:字节流和字符流。
字节流和字符流的区别是:字节流按 8 位传输以字节为单位输入输出数据,字符流按 16 位传输以字符为单位输入输出数据。
10.BIO、NIO、AIO 有什么区别?
BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。
NIO:New IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。
AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。
11.Files的常用方法都有哪些?
Files.exists():检测文件路径是否存在。
Files.delete():删除一个文件或目录。
Files.read():读取文件。
Files.write():写入文件。
6)wait和sleep的区别,必须理解
sleep方法属于线程,wait方法属于对象
sleep休眠当前线程,不会释放对象锁,wait使当前线程进入等待状态,释放对象锁,只有针对此对象调用notify()方法(且共享对象资源释放)后本线程才会继续执行
7)JVM的内存结构,JVM的算法
JVM内存结构主要有三大块:堆内存、方法区和栈,
几乎所有的对象实例都存放在堆里,如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。
方法区用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。
每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。
每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常。
15)说说http,https协议
http是一种超文本协议,默认端口80,以明文传输。
https是http协议的安全版,安全基础是SSL,以密文传输
16)osi五层网络协议
应用层、传输层、网络层、数据链路层、物理层
18)说说tcp三次握手,四次挥手
1.客户端向服务器发送一个syn包,进入发送状态
2.服务器收到syn包,确认客户的syn,并向客户端发送syn+ack包,进入接受状态
3.客户端接受的来自服务的的syn包信息,向服务的发出ack包,次数两者进入tcp连接成功状态
21)请写一段栈溢出、堆溢出的代码
堆溢出,死循环存值,JVM就会抛出OutOfMemoryError:java heap space异常
1 | public static void main(String[] args) { |
栈溢出,栈空间不足——StackOverflowError实例
1 | public class StackSOFTest { |
29.说出一些常用的类,包,接口,请各举5个。
注意:要让人家感觉你对java ee开发很熟,java中的那些东西,要多列你在做ssh项目中涉及的那些东西。就写你最近写的那些程序中涉及的那些类。
常用的类:
BufferedReader
BufferedWriter
FileReader
FileWirter
String
Integer
java.util.Date
System
Class
List
HashMap
常用的包:
java.lang
java.io
java.util
java.sql
javax.servlet
org.apache.strtuts.action
org.hibernate
常用的接口:
Remote
List
Map
Document
NodeList
Servlet
HttpServletRequest
HttpServletResponse
Transaction(Hibernate)
Session(Hibernate)
HttpSession
33.描述一下JVM加载class文件的原理机制?
Java语言是一种具有动态性的解释型语言,类(class)只有被加载到JVM后才能运行。当运行指定程序时,JVM会将编译生成的.class文件按照需求和一定的规则加载到内存中,并组织成为一个完整的Java应用程序,这个加载过程是由类加载器完成。
具体来说,就是由ClassLoader和它的子类来实现的。类加载器本身也是一个类,其实质是把类文件从硬盘读取到内存中。
类的加载方式分为隐式加载和显示加载。隐式加载指的是程序在使用new等方式创建对象时,会隐式地调用类的加载器把对应的类加载到JVM中。显示加载指的是通过直接调用class.forName()方法来把所需的类加载到JVM中。
任何一个工程项目都是由许多类组成的,当程序启动时,只把需要的类加载到JVM中,其他类只有被使用到的时候才会被加载,采用这种方法一方面可以加快加载速度,另一方面可以节约程序运行时对内存的开销。
此外,在Java语言中,每个类或接口都对应一个.class文件,这些文件可以被看成是一个个可以被动态加载的单元,因此当只有部分类被修改时,只需要重新编译变化的类即可,而不需要重新编译所有文件,因此加快了编译速度。
在Java语言中,类的加载是动态的,它并不会一次性将所有类全部加载后再运行,而是保证程序运行的基础类(例如基类)完全加载到JVM中,至于其他类,则在需要的时候才加载:下面是加载的步骤:
初始化。对静态变量和静态代码块执行初始化工作。
装载。根据查找路径找到相应的class文件,然后导入。
链接。链接又可分为3个小步:
检查,检查待加载的class文件的正确性。
准备,给类中的静态变量分配存储空间。
解析,将符号引用转换为直接引用(这一步可选)。
方法中的局部变量使用final修饰后,放在堆中,而不是栈
35.堆和栈的区别,有一个64k的字符串,是放到堆上,还是放到栈上,为什么?
堆栈都是内存的可用区域,但是堆的速度慢容量大,栈的速度快容量小。一个64K的字符串,自然放在堆。
栈的内存是很宝贵的。
只有引用及基本数据类型是直接存在栈上。对象类型可能是在堆、方法区、常量池中;放到堆中还是放到栈中,jvm会根据你的数据类型决定。
栈区(stack): 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
堆区(heap) :一般由程序员分配释放, 若程序员不释放,程序结束时可能由GC回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
36.GC是什么? 为什么要有GC?
GC是垃圾收集的意思(Gabage Collection),内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃。
Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显示操作方法。
38.垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收?
对于GC来说,当程序员创建对象时,GC就开始监控这个对象的地址、大小以及使用情况。
通常,GC采用有向图的方式记录和管理堆(heap)中的所有对象。通过这种方式确定哪些对象是”可达的”,哪些对象是”不可达的”。当GC确定一些对象为”不可达”时,GC就有责任回收这些内存空间。
不过垃圾回收机制的回收是不确定的,不一定会马上回收内存。
可以主动通知虚拟机进行垃圾回收:程序员可以手动执行System.gc(),通知GC运行,但是Java语言规范并不保证GC一定会执行。实际上GC是一个守护线程(守护线程的作用是为其他线程提供服务)。
39.什么时候用assert?
assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。
在实现中,assertion就是在程序中的一条语句,它对一个boolean表达式进行检查,一个正确程序必须保证这个boolean表达式的值为true;如果该值为false,说明程序已经处于不正确的状态下,assert将给出警告或退出。
一般来说,assertion用于保证程序最基本、关键的正确性。assertion检查通常在开发和测试时开启。为了提高性能,在软件发布后,assertion检查通常是关闭的。
1 | public class AssertTest { |
40.java中会存在内存泄漏吗,请简单描述。
所谓内存泄露就是指一个不再被程序使用的对象或变量一直被占据在内存中。java中有垃圾回收机制,它可以保证一对象不再被引用的时候,即对象变成了孤儿的时候,对象将自动被垃圾回收器从内存中清除掉。
由于Java 使用有向图的方式进行垃圾回收管理,可以消除引用循环的问题,例如有两个对象,相互引用,只要它们和根进程不可达的,那么GC也是可以回收它们的,例如下面的代码可以看到这种情况的内存回收:
java中的内存泄露的情况:长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是java中内存泄露的发生场景(通俗地说,就是程序员可能创建了一个对象,以后一直不再使用这个对象,这个对象却一直被引用,即这个对象无用但是却无法被垃圾回收器回收的)。
如果一个外部类的实例对象的方法返回了一个内部类的实例对象,这个内部类对象被长期引用了,即使那个外部类实例对象不再被使用,但由于内部类持久外部类的实例对象,这个外部类对象将不会被垃圾回收,这也会造成内存泄露。
检查java中的内存泄露,一定要让程序将各种分支情况都完整执行到程序结束,然后看某个对象是否被使用过,如果没有,则才能判定这个对象属于内存泄露。
内存泄露的另外一种情况:当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中单独删除当前对象,造成内存泄露。
java中可能出现内存泄露的情况,例如,缓存系统,我们加载了一个对象放在缓存中(例如放在一个全局map对象中),然后一直不再使用它,这个对象一直被缓存引用,但却不再被使用。
41.能不能自己写个类,也叫java.lang.String?
可以,但在应用的时候,需要用自己的类加载器去加载,否则,系统的类加载器永远只是去加载jre.jar包中的那个java.lang.String。
194.说一下 jvm 的主要组成部分?及其作用?
类加载器(ClassLoader)
运行时数据区(Runtime Data Area)
执行引擎(Execution Engine)
本地库接口(Native Interface)
组件的作用: 首先通过类加载器(ClassLoader)会把 Java 代码转换成字节码,运行时数据区(Runtime Data Area)再把字节码加载到内存中,而字节码文件只是 JVM 的一套指令集规范,并不能直接交个底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由 CPU 去执行,而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。
197.队列和栈是什么?有什么区别?
队列和栈都是被用来预存储数据的。
队列允许先进先出检索元素,但也有例外的情况,Deque 接口允许从两端检索元素。
栈对元素进行后进先出进行检索。
198.什么是双亲委派模型?
在介绍双亲委派模型之前先说下类加载器。对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立在 JVM 中的唯一性,每一个类加载器,都有一个独立的类名称空间。类加载器就是根据指定全限定名称将 class 文件加载到 JVM 内存,然后再转化为 class 对象。
类加载器分类:
启动类加载器(Bootstrap ClassLoader),是虚拟机自身的一部分,用来加载Java_HOME/lib/目录中的,或者被 -Xbootclasspath 参数所指定的路径中并且被虚拟机识别的类库;
其他类加载器:
扩展类加载器(Extension ClassLoader):负责加载<java_home style=“box-sizing: border-box; -webkit-tap-highlight-color: transparent; text-size-adjust: none; -webkit-font-smoothing: antialiased; outline: 0px !important;”>\lib\ext目录或Java. ext. dirs系统变量指定的路径中的所有类库;</java_home>
应用程序类加载器(Application ClassLoader)。负责加载用户类路径(classpath)上的指定类库,我们可以直接使用这个类加载器。一般情况,如果我们没有自定义类加载器默认就是用这个加载器。
双亲委派模型:如果一个类加载器收到了类加载的请求,它首先不会自己去加载这个类,而是把这个请求委派给父类加载器去完成,每一层的类加载器都是如此,这样所有的加载请求都会被传送到顶层的启动类加载器中,只有当父加载无法完成加载请求(它的搜索范围中没找到所需的类)时,子加载器才会尝试去加载类。
\199. 说一下类加载的执行过程?
类加载分为以下 5 个步骤:
加载:根据查找路径找到相应的 class 文件然后导入;
检查:检查加载的 class 文件的正确性;
准备:给类中的静态变量分配内存空间;
解析:虚拟机将常量池中的符号引用替换成直接引用的过程。符号引用就理解为一个标示,而在直接引用直接指向内存中的地址;
初始化:对静态变量和静态代码块执行初始化工作。
200.怎么判断对象是否可以被回收?
一般有两种方法来判断:
引用计数器:为每个对象创建一个引用计数,有对象引用时计数器 +1,引用被释放时计数 -1,当计数器为 0 时就可以被回收。它有一个缺点不能解决循环引用的问题;
可达性分析:从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是可以被回收的。
\201. java 中都有哪些引用类型?
强引用
软引用
弱引用
虚引用
\202. 说一下 jvm 有哪些垃圾回收算法?
标记-清除算法
标记-整理算法
复制算法
分代算法
203.说一下 jvm 有哪些垃圾回收器?
Serial:最早的单线程串行垃圾回收器。
Serial Old:Serial 垃圾回收器的老年版本,同样也是单线程的,可以作为 CMS 垃圾回收器的备选预案。
ParNew:是 Serial 的多线程版本。
Parallel 和 ParNew 收集器类似是多线程的,但 Parallel 是吞吐量优先的收集器,可以牺牲等待时间换取系统的吞吐量。
Parallel Old 是 Parallel 老生代版本,Parallel 使用的是复制的内存回收算法,Parallel Old 使用的是标记-整理的内存回收算法。
CMS:一种以获得最短停顿时间为目标的收集器,非常适用 B/S 系统。
G1:一种兼顾吞吐量和停顿时间的 GC 实现,是 JDK 9 以后的默认 GC 选项。
204.详细介绍一下 CMS 垃圾回收器?
CMS 是英文 Concurrent Mark-Sweep 的简称,是以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器。对于要求服务器响应速度的应用上,这种垃圾回收器非常适合。在启动 JVM 的参数加上“-XX:+UseConcMarkSweepGC”来指定使用 CMS 垃圾回收器。
CMS 使用的是标记-清除的算法实现的,所以在 gc 的时候回产生大量的内存碎片,当剩余内存不能满足程序运行要求时,系统将会出现 Concurrent Mode Failure,临时 CMS 会采用 Serial Old 回收器进行垃圾清除,此时的性能将会被降低。
205.新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么区别?
新生代回收器:Serial、ParNew、Parallel Scavenge
老年代回收器:Serial Old、Parallel Old、CMS
整堆回收器:G1
新生代垃圾回收器一般采用的是复制算法,复制算法的优点是效率高,缺点是内存利用率低;
老年代回收器一般采用的是标记-整理的算法进行垃圾回收。
\206. 简述分代垃圾回收器是怎么工作的?
分代回收器有两个分区:老生代和新生代,新生代默认的空间占比总空间的 1/3,老生代的默认占比是 2/3。
新生代使用的是复制算法,新生代里有 3 个分区:Eden、To Survivor、From Survivor,它们的默认占比是 8:1:1,它的执行流程如下:
把 Eden + From Survivor 存活的对象放入 To Survivor 区;
清空 Eden 和 From Survivor 分区;
From Survivor 和 To Survivor 分区交换,From Survivor 变 To Survivor,To Survivor 变 From Survivor。
每次在 From Survivor 到 To Survivor 移动时都存活的对象,年龄就 +1,当年龄到达 15(默认配置是 15)时,升级为老生代。大对象也会直接进入老生代。
老生代当空间占用到达某个值之后就会触发全局垃圾收回,一般使用标记整理的执行算法。以上这些循环往复就构成了整个分代垃圾回收的整体执行流程。
207.说一下 jvm 调优的工具?
JDK 自带了很多监控工具,都位于 JDK 的 bin 目录下,其中最常用的是 jconsole 和 jvisualvm 这两款视图监控工具。
jconsole:用于对 JVM 中的内存、线程和类等进行监控;
jvisualvm:JDK 自带的全能分析工具,可以分析:内存快照、线程快照、程序死锁、监控内存的变化、gc 变化等。
\208. 常用的 jvm 调优的参数都有哪些?
-Xms2g:初始化推大小为 2g;
-Xmx2g:堆最大内存为 2g;
-XX:NewRatio=4:设置年轻的和老年代的内存比例为 1:4;
-XX:SurvivorRatio=8:设置新生代 Eden 和 Survivor 比例为 8:2;
–XX:+UseParNewGC:指定使用 ParNew + Serial Old 垃圾回收器组合;
-XX:+UseParallelOldGC:指定使用 ParNew + ParNew Old 垃圾回收器组合;
-XX:+UseConcMarkSweepGC:指定使用 CMS + Serial Old 垃圾回收器组合;
-XX:+PrintGC:开启打印 gc 信息;
-XX:+PrintGCDetails:打印 gc 详细信息。
2.你所知道网络协议有那些?
HTTP:超文本传输协议
FTP:文件传输协议
SMPT:简单邮件协议
TELNET:远程终端协议
POP3:邮件读取协议
3.Java都有那些开发平台?
JAVA SE:主要用在客户端开发
JAVA EE:主要用在web应用程序开发
JAVA ME:主要用在嵌入式应用程序开发
5.Java是否需要开发人员回收内存垃圾吗?
大多情况下是不需要的。Java提供了一个系统级的线程来跟踪内存分配,不再使用的内存区将会自动回收
8.Java的数据结构有那些?
线性表(ArrayList)
链表(LinkedList)
栈(Stack)
队列(Queue)
图(Map)
树(Tree)
9.什么是OOP?
面向对象编程
10.什么是面向对象?
世间万物都可以看成一个对象。每个物体包括动态的行为和静态的属性,这些就构成了一个对象。
11.类与对象的关系?
类是对象的抽象,对象是类的具体,类是对象的模板,对象是类的实例
12.Java中有几种数据类型
整形:byte,short,int,long
浮点型:float,double
字符型:char
布尔型:boolean
13.什么是隐式转换,什么是显式转换
显示转换就是类型强转,把一个大类型的数据强制赋值给小类型的数据;隐式转换就是大范围的变量能够接受小范围的数据;隐式转换和显式转换其实就是自动类型转换和强制类型转换。
14.Char类型能不能转成int类型?能不能转化成string类型,能不能转成double类型
Char在java中也是比较特殊的类型,它的int值从1开始,一共有2的16次方个数据;Char<int<long<float<double;Char类型可以隐式转成int,double类型,但是不能隐式转换成string;如果char类型转成byte,short类型的时候,需要强转。
15.什么是拆装箱?
拆箱:把包装类型转成基本数据类型
装箱:把基本数据类型转成包装类型
16.Java中的包装类都是那些?
byte:Byte
short:Short
int:Integer
long:Long
float:Float
double:Double
char:Character
boolean:Boolean
17.一个java类中包含那些内容?
属性、方法、内部类、构造方法、代码块。
18.例如: if(a+1.0=4.0),这样做好吗?
不好,因为计算机在浮点型数据运算的时候,会有误差,尽量在布尔表达式中不使用浮点型数据(if,while,switch中判断条件不使用浮点型)
19.那针对浮点型数据运算出现的误差的问题,你怎么解决?
使用Bigdecimal
类进行浮点型数据的运算
20.++i与i++的区别
++i:先赋值,后计算
i++:先计算,后赋值
21.程序的结构有那些?
顺序结构
选择结构
循环结构
22.数组实例化有几种方式?
静态实例化:创建数组的时候已经指定数组中的元素,
int[] a=new int[]{1,3,3}
动态实例化:实例化数组的时候,只指定了数组程度,数组中所有元素都是数组类型的默认值
23.Java中各种数据默认值
Byte,short,int,long默认是都是0
Boolean默认值是false
Char类型的默认值是’ ’
Float与double类型的默认是0.0
对象类型的默认值是null
26.Object类常用方法有那些?
Equals
Hashcode
toString
wait
notify
clone
getClass
28.java中是值传递引用传递?
理论上说,java都是引用传递,对于基本数据类型,传递是值的副本,而不是值本身。对于对象类型,传递是对象的引用,当在一个方法操作操作参数的时候,其实操作的是引用所指向的对象。
29.假设把实例化的数组的变量当成方法参数,当方法执行的时候改变了数组内的元素,那么在方法外,数组元素有发生改变吗?
改变了,因为传递是对象的引用,操作的是引用所指向的对象
30.实例化数组后,能不能改变数组长度呢?
不能,数组一旦实例化,它的长度就是固定的
31.假设数组内有5个元素,如果对数组进行反序,该如何做?
创建一个新数组,从后到前循环遍历每个元素,将取出的元素依次顺序放入新数组中
32.形参与实参
形参:全称为“形式参数”,是在定义方法名和方法体的时候使用的参数,用于接收调用该方法时传入的实际值;
实参:全称为“实际参数”,是在调用方法时传递给该方法的实际值。
33.构造方法能不能显式调用?
不能 构造方法当成普通方法调用,只有在创建对象的时候它才会被系统调用
36.内部类与静态内部类的区别?
静态内部类相对与外部类是独立存在的,在静态内部类中无法直接访问外部类中变量、方法。如果要访问的话,必须要new一个外部类的对象,使用new出来的对象来访问。但是可以直接访问静态的变量、调用静态的方法;
普通内部类作为外部类一个成员而存在,在普通内部类中可以直接访问外部类属性,调用外部类的方法。
如果外部类要访问内部类的属性或者调用内部类的方法,必须要创建一个内部类的对象
37.Static关键字有什么作用?
Static可以修饰内部类、方法、变量、代码块
Static修饰的类是静态类
Static修饰的方法是静态方法,表示该方法属于当前类的,而不属于某个对象的,静态方法也不能被重写,可以直接使用类名来调用。在static方法中不能使用this或者super关键字。
Static修饰变量是静态变量或者叫类变量,静态变量被所有实例所共享,不会依赖于对象。静态变量在内存中只有一份拷贝,在JVM加载类的时候,只为静态分配一次内存。
Static修饰的代码块叫静态代码块,通常用来做程序优化的。静态代码块中的代码在整个类加载的时候只会执行一次。静态代码块可以有多个,如果有多个,按照先后顺序依次执行。
38.Final在java中的作用
Final可以修饰类,修饰方法,修饰变量。
修饰的类叫最终类。该类不能被继承。
修饰的方法不能被重写。
修饰的变量叫常量,常量必须初始化,一旦初始化后,常量的值不能发生改变。
39.Java中操作字符串使用哪个类?
String,StringBuffer,StringBuilder
42.String str=”aa”,String s=”bb”,String aa=aa+s;一种创建了几个对象?
一共有两个引用,三个对象。因为”aa”与”bb”都是常量,常量的值不能改变,当执行字符串拼接时候,会创建一个新的常量是” aabbb”,有将其存到常量池中。
43.将下java中的math类有那些常用方法?
Pow():幂运算
Sqrt():平方根
Round():四舍五入
Abs():求绝对值
Random():生成一个0-1的随机数,包括0不包括1
46.==与equlas有什么区别?
==可以判断基本数据类型值是否相等,也可以判断两个对象指向的内存地址是否相同,也就是说判断两个对象是否是同一个对象,Equlas通常用来做字符串比较。
53.创建一个子类对象的时候,那么父类的构造方法会执行吗?
会执行。当创建一个子类对象,调用子类构造方法的时候,子类构造方法会默认调用父类的构造方法。
54.什么是父类引用指向子类对象?
是java多态一种特殊的表现形式。创建父类引用,让该引用指向一个子类的对象
55.当父类引用指向子类对象的时候,子类重写了父类方法和属性,那么当访问属性的时候,访问是谁的属性?调用方法时,调用的是谁的方法?
子类重写了父类方法和属性,访问的是父类的属性,调用的是子类的方法
60.抽象类可以使用final修饰吗?
不可以。定义抽象类就是让其他继承的,而final修饰类表示该类不能被继承,与抽象类的理念违背了
64.接口有什么特点?
接口中声明全是public static final修饰的常量
接口中所有方法都是抽象方法
接口是没有构造方法的
接口也不能直接实例化
接口可以多继承
68.异常的处理机制有几种?
异常捕捉:try…catch…finally,异常抛出:throws。
69.如何自定义一个异常
继承一个异常类,通常是RumtimeException或者Exception
70.在异常捕捉时,如果发生异常,那么try.catch.finally块外的return语句会执行吗?
会执行,如果有finally,在finally之后被执行,如果没有finally,在catch之后被执行
72.Thow与thorws区别
Throw写在代码块内,throws后面跟的是一个具体的异常实例
Throw写在方法前面后面,throws后面跟的是异常类,异常类可以出现多个
74.使用Log4j对程序有影响吗?
有,log4j是用来日志记录的,记录一些关键敏感的信息,通常会将日志记录到本地文件或者数据库中。记录在本地文件中,会有频繁的io操作,会耗费一些系统资源。记录在数据库中,会频繁地操作数据库表,对系统性能也有一定的影响。但是为了程序安全以及数据的恢复或者bug的跟踪,这点资源消耗是可以承受的。
75.Log4j日志有几个级别?
由低到高:debug、info、wran、error
77.Java反射创建对象效率高还是通过new创建对象的效率高?
通过new创建对象的效率比较高。通过反射时,先找查找类资源,使用类加载器创建,过程比较繁琐,所以效率较低
85.JDBC操作的步骤
加载数据库驱动类
打开数据库连接
执行sql语句
处理返回结果
关闭资源
86.在使用jdbc的时候,如何防止出现sql注入的问题。
使用PreparedStatement类,而不是使用Statement类
87.怎么在JDBC内调用一个存储过程
使用CallableStatement
88.是否了解连接池,使用连接池有什么好处?
数据库连接是非常消耗资源的,影响到程序的性能指标。
连接池是用来分配、管理、释放数据库连接的,可以使应用程序重复使用同一个数据库连接,而不是每次都创建一个新的数据库连接。通过释放空闲时间较长的数据库连接避免数据库因为创建太多的连接而造成的连接遗漏问题,提高了程序性能。
89.你所了解的数据源技术有那些?使用数据源有什么好处?
Dbcp,c3p0等,用的最多还是c3p0,因为c3p0比dbcp更加稳定,安全;通过配置文件的形式来维护数据库信息,而不是通过硬编码。
当连接的数据库信息发生改变时,不需要再更改程序代码就实现了数据库信息的更新。
91.常用io类有那些?
File
FileInputSteam,FileOutputStream
BufferInputStream,BufferedOutputSream
Print Write
FileReader,FileWriter
BufferReader,BufferedWriter
92.字节流与字符流的区别
以字节为单位输入输出数据,字节流按照8位传输
以字符为单位输入输出数据,字符流按照16位传输
93.final、finalize()、finally
性质不同
final为关键字;
finalize()为方法;
finally为区块标志,用于try语句中;
作用
final为用于标识常量的关键字,final标识的关键字存储在常量池中(在这里final常量的具体用法将在下面进行介绍);
finalize()方法在Object中进行了定义,用于在对象“消失”时,由JVM进行调用用于对对象进行垃圾回收,类似于C++中的析构函数;用户自定义时,用于释放对象占用的资源(比如进行I/0操作);
finally{}用于标识代码块,与try{}进行配合,不论try中的代码执行完或没有执行完(这里指有异常),该代码块之中的程序必定会进行;
99.如果对象的引用被置为null,垃圾收集器是否会立即释放对象占用的内存?
不会,在下一个垃圾回收周期中,这个对象将是可被回收的。
10、什么是 ThreadLocal?ThreadLocal 和 Synchonized 的区别?
线程局部变量。是局限于线程内部的变量,属于线程自身所有,不在多个线程间共享。Java提供 ThreadLocal 类来支持线程局部变量,是一种实现线程安全的方式。
synchronized 是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而 ThreadLocal 为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。
6.深拷贝和浅拷贝:
简单来讲就是复制、克隆;Person p=new Person(“张三”);
浅拷贝就是对对象中的数据成员进行简单赋值,如果存在动态成员或者指针就会报错
深拷贝就是对对象中存在的动态成员或指针重新开辟内存空间
7.值传递和引用传递:
值传递是针对基本数据类型而言,传递的是值得副本,对副本的改变不会影响到原变量
引用传递:就是将一个堆内存空间的使用权交给多个栈内存空间,每一个栈内存空间都可以对堆内存空间进行修改
8.web 容器功能:
通信支持、管理Servlet生命周期,多线程、将jsp转换成java等等
9.java内存分配
寄存器:我们无法控制
静态域:static定义的静态成员
常量池:编译时被确定并保存在.class文件中的(final)常量值和一些文本修饰的符号引用(类和接口的全限定名,字段的名称和描述符,方法和名称和描述符)
非ram存储:硬盘等永久存储空间
堆内存:new创建的对象和数组,由java虚拟机自动垃圾回收器管理,存取速度慢
栈内存:基本类型的变量和对象的引用变量(堆内存空间的访问地址),速度快,可以共享,但是大小与生存期必须确定,缺乏灵活性
11.一个”.java”源文件中是否可以包括多个类(不是内部类)?有什么限制?
可以有多个类,但只能有一个public的类,并且public的类名必须与文件名相一致。
13.简述逻辑操作(&,|,^)与条件操作(&&,||)的区别。
条件操作只能操作布尔型的,而逻辑操作不仅可以操作布尔型,而且可以操作数值型
逻辑操作不会产生短路.
使用逻辑操作符时,我们会遇到一种“短路”现象。即一旦能够明确无误地确定整个表达式的值,就不再计算表达式余下部分了。因此,整个逻辑表达式靠后的部分有可能不会被运算
14.说说&和&&的区别。
==相同点==:&和&&都可以用作逻辑与的运算符,表示逻辑与(and),当运算符两边的表达式的结果都为true时,整个运算结果才为true,否则,只要有一方为false,则结果为false。
==&&具有短路功能==:即如果第一个表达式为false,则不再计算第二个表达式。
15.switch语句能否作用在byte上,能否作用在long上,能否作用在String上?
在switch(expr1)中,expr1只能是一个整数表达式或者枚举常量(更大字体),整数表达式可以是int基本类型或Integer包装类型,由于,byte,short,char都可以隐含转换为int,所以,这些类型以及这些类型的包装类型也是可以的。long不符合switch的语法规定,并且不能被隐式转换成int类型,所以,它不能作用于swtich语句中。
对于string而言:JDK1.7以前是不能作为switch的,以后即可以作用于switch中。
17.char型变量中能不能存贮一个中文汉字?为什么?
char型变量是用来存储Unicode编码的字符的,unicode编码字符集中包含了汉字,所以,char型变量中当然可以存储汉字。
不过要注意的是:
如果某个特殊的汉字没有被包含在unicode编码字符集中,那么,这个char型变量中就不能存储这个特殊汉字
unicode编码占用两个字节,所以,char类型的变量也是占用两个字节。
18.用最有效率的方法算出2乘以8等於几?
方法:将一个数左移n位,就相当于乘以了2的n次方,那么,一个数乘以8(2的3次方),只要将其左移3位即可,而位运算cpu直接支持的,效率最高,所以,2乘以8等於几的最效率的方法是将2左移3位,即2 << 3。
19.使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变?
使用final关键字修饰一个变量时,是指引用变量不能变,引用变量所指向的对象中的内容还是可以改变的。例如,对于如下语句:final StringBuffer a=new StringBuffer(“immutable”);
执行如下语句将报告编译期错误:a=new StringBuffer(“”);
但是,执行如下语句则可以通过编译:a.append(“ broken!”);
java有人在定义方法的参数时,可能想采用如下形式来阻止方法内部修改传进来的参数对象:
public void method(final StringBuffer param) { }
实际上,这是办不到的,在该方法内部仍然可以增加如下代码来修改参数对象: param.append(“a”);
20.java 中对象的创建方法有几种?
通过new来创建
通过反射创建
通过复制创建
21.”==”和equals方法究竟有什么区别?
如果是在object对象中,那么两者所表示的都是值是否相等(public boolean equals(Object obj){return (this==obj); }),可以看到object的equals方法是使用的“==”比较。
一般而言,要比较两个基本类型的数据或两个引用变量是否相等,只能用==操作符,也就是比较内存中所存储的两个变量的值是否相等。
举例:String a=new String(“foo”); String b=new String(“foo”);它们的首地址是不同的,即a和b中存储的数值是不相同的,所以,表达式a==b将返回false,而这两个对象中的内容是相同的,所以,表达式a.equals(b)将返回true。
在实际开发中,我们经常要比较传递进行来的字符串内容是否等,此时是使用的equals()方法。
对于equals()而言,默认情况是从object类继承的,当希望能够比较该类创建的两个实例对象的内容是否相同,那么你必须覆盖equals方法,由自己写代码来决定在什么情况即可认为两个对象的内容是相同的。
22.静态变量和实例变量的区别?
在语法定义上的区别:静态变量前要加static关键字,而实例变量前则不加。
在程序运行时的区别:
实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。
静态变量不属于某个实例对象,而是属于类,所以也称为类变量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了。、
总之,实例变量必须创建对象后才可以通过这个对象来使用,静态变量则可以直接使用类名来引用。
23.是否可以从一个static方法内部发出对非static方法的调用?
不可以。
对于static修饰的静态方法,是随着类的加载而加载,且调用的时可以不用创建对象而直接调用
非静态方法要与对象联系在一起,只有创建了对象了以后才能调用非静态方法,即当一个静态方法被调用的时候,有可能还没有创建任何实例对象。
24.Integer与int的区别
int是java提供的8种原始数据类型之一,系统给的默认值为0。
Java为每个原始类型提供了封装类,Integer是java为int提供的封装类。系统给的默认值为null。
25.Math.round(11.5)等於多少? Math.round(-11.5)等於多少?
Math类中提供了三个与取整有关的方法:ceil(向上取整)、floor(向下取整)、round(四舍五入)
举例:
Math.ceil(11.3)的结果为12,Math.ceil(-11.3)的结果是-11
Math.floor(11.6)的结果为11,Math.floor(-11.6)的结果是-12
算法为Math.floor(x+0.5),即将原来的数字加上0.5后再向下取整,所以,Math.round(11.5)的结果为12,Math.round(-11.5)的结果为-11。
28.Overload和Override的区别。Overload的方法是否可以改变返回值的类型?
Overload:表示方法重载,表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同(即参数个数、类型、位置不同),通过定义不同的输入参数来区分这些方法,然后再调用时,JVM就会根据不同的参数样式,来选择合适的方法执行:
在使用重载时只能通过不同的参数样式。例如,不同的参数类型,不同的参数个数,不同的参数顺序(当然,同一方法内的几个参数类型必须不一样,例如可以是fun(int,float),但是不能为fun(int,int));
不能通过访问权限、返回类型、抛出的异常进行重载;
方法的异常类型和数目不会对重载造成影响;
对于继承来说,如果某一方法在父类中是访问权限是priavte,那么就不能在子类对其进行重载,如果定义的话,也只是定义了一个新方法,而不会达到重载的效果。
Override:表示方法重写(覆盖),表示子类中的方法可以与父类中的某个方法的名称和参数完全相同,当通过子类创建的实例对象调用这个方法时,将调用子类中的定义方法,这相当于把父类中定义的那个完全相同的方法给覆盖了,这也是面向对象编程的多态性的一种表现。
重写的方法的标志必须要和被重写的方法的标志完全匹配,才能达到重写的效果;
重写的方法的返回值必须和被重写的方法的返回一致;
重写的方法所抛出的异常必须和被重写方法的所抛出的异常一致,或者是其子类(因为子类是解决父类的一些方法,不能比父类更多问题);
被重写的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行重写(子类方法的访问权限只能比父类的更大,不能更小)。
如果房子有多个椅子,就是聚合关系,否则是一种关联关系,当然,聚合是一种特殊的关联。椅子与腿和背时组合关系。
31.序列化接口的id有什么用?
对象经常要通过IO进行传送,让你写程序传递对象,你会怎么做?把对象的状态数据用某种格式写入到硬盘,Person->“zxx,male,28,30000”Person,既然大家都要这么干,并且没有个统一的干法,于是,sun公司就提出一种统一的解决方案,它会把对象变成某个格式进行输入和输出,这种格式对程序员来说是透明(transparent)的,但是,我们的某个类要想能被sun的这种方案处理,必须实现Serializable接口。ObjectOutputStream.writeObject(obj);
Object obj = ObjectInputStream.readObject();
假设两年前我保存了某个类的一个对象,这两年来,我修改该类,删除了某个属性和增加了另外一个属性,两年后,我又去读取那个保存的对象,或有什么结果?未知!sun的jdk就会蒙了。为此,一个解决办法就是在类中增加版本后,每一次类的属性修改,都应该把版本号升级一下,这样,在读取时,比较存储对象时的版本号与当前类的版本号,如果不一致,则直接报版本号不同的错!
32.hashCode方法的作用?
hashcode这个方法是用来鉴定2个对象是否相等的。
与equals()方法的区别:
简单来讲,equals方法主要是用来判断从表面上看或者从内容上看,2个对象是不是相等,equals这个方法是给用户调用的,如果你想判断2个对象是否相等,你可以重写equals方法,然后在代码中调用,就可以判断他们是否相等了
hashcode方法一般用户不会去调用,比如在hashmap中,由于key是不可以重复的,他在判断key是不是重复的时候就判断了hashcode这个方法,而且也用到了equals方法。
hashcode相当于是一个对象的编码,他和equals不同就在于他返回的是int型的,比较起来不直观。我们一般在覆盖equals的同时也要覆盖hashcode,让他们的逻辑一致。
33.构造器Constructor是否可被override?
重写发生在继承过程中(子父类间),但是构造器Constructor不能被继承,因此不能重写Override,但可以被重载Overload。
6.final, finally, finalize的区别。
final:用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承,内部类要访问局部变量,局部变量必须定义成final类型。
finally:是异常处理语句结构的一部分,表示总是执行,除非是遇到重大错误error,才不会执行finally。
finalize:是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。JVM不保证此方法总被调用。
9.Java中的异常处理机制的简单原理和应用。
异常是指java程序运行时(非编译)所发生的非正常情况或错误,Java使用面向对象的方式来处理异常,它把程序中发生的每s个异常也都分别封装到一个对象来表示的,该对象中包含有异常的信息。
所有异常的根类为java.lang.Throwable:Throwable下面又派生了两个子类:Error和Exception。
Error 表示应用程序本身无法克服和恢复的一种严重问题,程序只有死的份了(内存溢出和线程死锁等系统问题)。
Exception表示程序还能够克服和恢复的问题:
其中又分为系统异常和普通异常,系统异常是软件本身缺陷所导致的问题,也就是软件开发人员考虑不周所导致的问题,软件使用者无法克服和恢复这种问题,但在这种问题下还可以让软件系统继续运行或者让软件死掉。
普通异常是运行环境的变化或异常所导致的问题,是用户能够克服的问题,例如,网络断线,硬盘空间不够,发生这样的异常后,程序不应该死掉。
java为系统异常和普通异常提供了不同的解决方案,编译器强制普通异常必须try..catch处理或用throws声明继续抛给上层调用方法处理,所以普通异常也称为checked异常,而系统异常可以处理也可以不处理,所以,编译器不强制用try..catch处理或用throws声明,所以系统异常也称为unchecked异常。
提示答题者:就按照三个级别去思考:虚拟机必须宕机的错误,程序可以死掉也可以不死掉的错误,程序不应该死掉的错误。
10.Throwable:异常的父类。
error :jvm严重错误,JVM无法继续,因此这是不可捕捉无法用程序去恢复的错误。
exception: 可以捕获到,可以恢复。
cheched exception:IO/SQL异常,JVM要求我们对出现的异常进行catch。
runtime exception:运行时异常,我们可以不处理,将其抛出最后可以抛给JVM处理,多线程由thread.run()抛出,单线程由main()函数抛出。运行时异常也有一般异常的子类,可以被catch到,如果不对其处理,要么线程终止,要么主线程终止(异常的处理目标就是将异常程序恢复正常)。
11.请写出你最常见到的5个runtime exception。
在jdk doc中查RuntimeException类,就可以看到其所有的子类列表,也就是看到了所有的系统异常。
ArrayindexOfBoundsException:数组越界异常。
NullPointerException:空指针异常。
ClassCastException:类型转换异常。
ClassNotFoundException:指定类不存在。
ArithmeticException:数字运算异常。
ArrayStoreException:数组存储与声明类型不兼容。
numberFormatException:数字格式异常。
12.Java语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗?
Java的异常处理是通过5个关键词来实现的:try、catch、throw、throws和finally。
try:指定一块预防所有”异常”的程序。
cache:紧跟在try程序后面,用来指定想要捕捉的”异常”的类型。
throw:不处理异常,直接明确地抛出一个”异常”,给上一层处理。
finally:确保一段代码不管发生什么”异常”都被执行一段代码。
16.同步有几种实现方法?
同步的实现方面有两种,分别是synchronized,wait与notify。
wait():使一个线程处于等待状态,并且释放所持有的对象的lock。
sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。
notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。
17.当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法?
其他方法前是否加了synchronized关键字,如果没加,则能。
如果这个方法内部调用了wait,则可以进入其他synchronized方法。
如果其他个方法都加了synchronized关键字,并且内部没有调用wait,则不能。
19.简述synchronized和java.util.concurrent.locks.Lock的异同?
主要相同点:Lock能完成synchronized所实现的所有功能。
主要不同点:Lock有比synchronized更精确的线程语义和更好的性能。
synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。
Lock还有更强大的功能,例如,它的tryLock方法可以非阻塞方式去拿锁。
3.Comparable和Comparator接口是干什么的。
Java提供了只包含一个compareTo()方法的Comparable接口。
这个方法可以个给两个对象排序。具体来说,它返回负数,0,正数来表明输入对象小于,等于,大于已经存在的对象。
Java提供了包含compare()和equals()两个方法的Comparator接口。
compare()方法用来给两个输入参数排序,返回负数,0,正数表明第一个参数是小于,等于,大于第二个参数。
1.写clone()方法时,通常都有一行代码,是什么
clone 有缺省行为,super.clone();因为首先要把父类中的成员复制到位,然后才是复制自己的成员。
2.面向对象的特征有哪些方面
==封装==:将对象封装成独立的,高度自治的,相对封闭的模块,实现“高内聚,低耦合”,降低相互的依赖性,这个对象状态(属性)由这个对象自己的行为(方法)来读取和改变,将属性(变量)定位private,只有这个类自己的方法才可以访问到这些成员变量,一个原则是:让方法和它待在一起。
==继承==:表示的是类与类的关系,是子类共享父类所有数据和方法的一种机制,可提高软件的可重用性和扩展性,缺点是加强了耦合性。
构造方法和private修饰的方法不可以被继承。
==多态==:程序运行时的多种状态,增强了软件的灵活性和扩展性。
==抽象==: 将事物的相似和共性之处,拿出来,然后将这些事物归为一个类,忽略与当前主题和目标无关的那些方面,将注意力集中在与当前目标有关的方面,抽象包括行为抽象和状态抽象两个方面。
把握一个原则:当前系统需要什么就只考虑什么。
2.java中实现多态的机制是什么?
靠的是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象,而程序调用的方法在运行期才动态绑定,就是引用变量所指向的具体实例对象的方法,也就是内存里正在运行的那个对象的方法,而不是引用变量的类型中定义的方法。
3.abstract class和interface有什么区别?
抽象类:含有abstract修饰符的class即为抽象类,abstract类不能创建的实例对象。
含有abstract方法的类必须定义为abstract class,abstract class类中的方法不必是抽象的。
abstract class类中定义抽象方法必须在具体(Concrete)子类中实现,==所以,不能有抽象构造方法或抽象静态方法==。
如果的子类没有实现抽象父类中的所有抽象方法,那么子类也必须定义为abstract类型。
接口:(interface)可以说成是抽象类的一种特例,接口中的所有方法都必须是抽象的。
- 接口中的方法定义默认为public abstract类型,接口中的成员变量类型默认为public static final。
==语法区别==:
抽象类可以有构造方法,接口中不能有构造方法。
抽象类中可以有普通成员变量,接口中没有普通成员变量
抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。
抽象类中的抽象方法的访问类型可以是public,protected和(默认类型,虽然eclipse下不报错,但应该也不行),==但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型==。
抽象类中可以包含静态方法,接口中不能包含静态方法
抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。
一个类可以实现多个接口,但只能继承一个抽象类。
==应用上的区别==:
接口更多的是在系统架构设计方法发挥作用,主要用于定义模块之间的通信契约
而抽象类在代码实现方面发挥作用,可以实现代码的重用
4.abstract的method是否可同时是static,是否可同时是native,是否可同时是synchronized?
abstract的method 不可以是static的,因为抽象的方法是要被子类实现的,而static与子类扯不上关系!
native方法表示该方法要用另外一种依赖平台的编程语言实现的,不存在着被子类实现的问题,所以,它也不能是抽象的,不能与abstract混用。
例如,FileOutputSteam类要硬件打交道,底层的实现用的是操作系统相关的api实现,例如,在windows用c语言实现的,所以,查看jdk的源代码,可以发现FileOutputStream的open方法的定义如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
如果我们要用java调用别人写的c语言函数,我们是无法直接调用的,我们需要按照java的要求写一个c语言的函数,用我们的这个c语言函数去调用别人的c语言函数。由于我们的c语言函数是按java的要求来写的,我们这个c语言函数就可以与java对接上,java那边的对接方式就是定义出与我们这个c函数相对应的方法,java中对应的方法不需要写具体的代码,==但需要在前面声明native==。
**关于synchronized与abstract合用的问题,我觉得也不行,synchronized应该是作用在一个具体的方法上才有意义,而且,方法上的synchronized同步所使用的同步锁对象是this,而抽象方法上无法确定this是什么。**
### 5.什么是内部类?Static Nested Class 和 Inner Class的不同。
首先内部类的总体方面的特点:例如,
在两个地方可以定义,可以访问外部类的成员变量,不能定义静态成员,这是大的特点。然后再说一些细节方面的知识,例如,几种定义方式的语法区别,静态内部类,以及匿名内部类。
**内部类就是在一个类的内部定义的类,内部类中不能定义静态成员**(静态成员不是对象的特性,只是为了找一个容身之处,所以需要放到一个类中而已
内部类可以直接访问外部类中的成员变量,内部类可以定义在外部类的方法外面,也可以定义在外部类的方法体中
**在方法内部定义的内部类前面不能有访问类型修饰符,**
### 6.内部类可以引用它的包含类的成员吗?有没有什么限制?
一般而言是可以的。如果不是静态内部类,那没有什么限制!
但是,如果把静态嵌套类当作内部类的一种特例,那在这种情况下不可以访问外部类的普通成员变量,而只能访问外部类中的静态成员
### 7.Anonymous Inner Class (匿名内部类)是否可以extends(继承)其它类,是否可以implements(实现)interface(接口)?
可以继承其他类或实现其他接口。不仅是可以,而是必须!
### 9.jdk中哪些类是不能继承的?
**不能继承的是类是那些用final关键字修饰的类**。一般比较基本的类型或防止扩展类无意间破坏原来方法的实现的类型都应该是final的,在jdk中比如,System,String,StringBuffer等类型。
### 10.String是最基本的数据类型吗?
不是,**是属于引用类型**。引用类型还有数组,日期等类型,java.lang.String类是final类型的,因此不可以继承这个类、不能修改这个类。为了提高效率节省空间,我们应该用StringBuffer类。
基本数据类型包括byte、int、char、long、float、double、boolean和short。
### 11.String s = "Hello";s = s + " world!";这两行代码执行后,原始的String对象中的内容到底变了没有?
**没有。因为String由final修饰,被设计成不可变(immutable)类,所以它的所有对象都是不可变对象。**
如果要使用内容相同的字符串,不必每次都new一个String。比如下面的例子,在构造器中对一个叫S的string引用变量进行初始化,将其设置为初始值:应该如下这样做
```java
public class Demo {
private String s;
...
public Demo {
s = "Initial Value";
}
...
}
而不是==s = new String(“Initial Value”)==,因为这样每次都会掉用新的构造器,生成新的对象,性能低下的同时,内存开销大
12.是否可以继承String类?
不能,String类是final类,故不可以继承。
13.String s = new String(“xyz”);创建了几个String Object? 二者之间有什么区别?
两个
”xyz”对应一个对象,这个对象放在==字符串常量缓冲区==,常量”xyz”不管出现多少遍,都是缓冲区中的那一个。
New String每写一遍,就创建一个新的对象,它依据那个常量”xyz”对象的内容来创建出一个新String对象
如果以前就用过’xyz’,就不会创建”xyz”自己了,直接从缓冲区拿。
14.String 和StringBuffer的区别
JAVA平台提供了两个类:String和StringBuffer,它们可以储存和操作字符串,即包含多个字符的字符数据。
String覆盖了equals方法和hashCode方法,而StringBuffer没有覆盖equals方法和hashCode方法,所以,将StringBuffer对象存储进Java集合类中时会出现问题。
String类: 表示内容不可改变的字符串,string重写了equals()方法,new String(“abc”).equals(new String(“abc”)的结果为true,
StringBuffer类: 表示内容可以被修改的字符串,因此当你知道字符数据要改变的时候你就可以使用==StringBuffer==(比如:你可以使用StringBuffer来动态构造字符数据),StringBuffer并没有实现equals()方法,故new StringBuffer(“abc”).equals(new StringBuffer(“abc”)的结果为false。
StringBuffer sbf = new StringBuffer();
for(int i=0;i<100;i++){
sbf.append(i);
}
上面的代码效率很高,因为只创建了一个StringBuffer对象,而下面的代码效率很低,因为创建了101个对象。
String str = new String();
for(int i=0;i<100;i++){
str = str + i;
}
在讲两者区别时,应把循环的次数搞成10000,然后用endTime-beginTime来比较两者执行的时间差异,最后还要讲讲StringBuilder与StringBuffer的区别。
15.StringBuffer与StringBuilder的区别
StringBuffer和StringBuilder类都表示内容可以被修改的字符串
StringBuilder是线程不安全的,运行效率高,如果一个字符串变量是在方法里面定义,这种情况只可能有一个线程访问它,不存在不安全的因素了,则用StringBuilder。
如果要在类里面定义成员变量,并且这个类的实例对象会在多线程环境下使用,那么最好用StringBuffer。
16.如何把一段逗号分割的字符串转换成一个数组?
用正则表达式,代码大概为:==String [] result = orgStr.split(“,”);==
用 StringTokenizer 代码为(麻烦):
1 | String orgStr = "aa,aa,dd,d"; |
\61. 为什么要使用克隆?
想对一个对象进行处理,又想保留原有的数据进行接下来的操作,就需要克隆了,Java语言中克隆针对的是类的实例。
\62. 如何实现对象克隆?
有两种方式:
1). 实现Cloneable接口并重写Object类中的clone()方法;
2). 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆,代码如下:
注意:基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用Object类的clone方法克隆对象。让问题在编译的时候暴露出来总是好过把问题留到运行时。
\63. 深拷贝和浅拷贝区别是什么?
浅拷贝只是复制了对象的引用地址,两个对象指向同一个内存地址,所以修改其中任意的值,另一个值都会随之变化,这就是浅拷贝(例:assign())
深拷贝是将对象及值复制过来,两个对象修改其中任意的值另一个值不会改变,这就是深拷贝(例:JSON.parse()和JSON.stringify(),但是此方法无法复制函数类型)
集合
12.Collection 和 Collections 有什么区别?
java.util.Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set。
Collections则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。
13.List、Set、Map 之间的区别是什么?
如果你经常会使用索引来对容器中的元素进行访问,那么 List 是你的正确的选择。
如果你已经知道索引了的话,那么 List 的实现类比如 ArrayList 可以提供更快速的访问,
如果经常添加删除元素的,那么肯定要选择LinkedList。
如果你想容器中的元素能够按照它们插入的次序进行有序存储,那么还是 List,因为 List 是一个有序容器,它按照插入顺序进行存储。
保证插入元素的唯一性,也就是你不想有重复值的出现,那么可以选择一个 Set 的实现类,比如 HashSet、LinkedHashSet 或者 TreeSet。所有 Set 的实现类都遵循了统一约束比如唯一性,而且还提供了额外的特性比如 TreeSet 还是一个 SortedSet,所有存储于 TreeSet 中的元素可以使用 Java 里的 Comparator 或者 Comparable 进行排序。LinkedHashSet 也按照元素的插入顺序对它们进行存储。
如果你以键和值的形式进行数据存储那么 Map 是你正确的选择。你可以根据你的后续需要从 Hashtable、HashMap、TreeMap 中进行选择。
14.java的关键字(keyword)有多少个?
51+2个保留字=53个关键字 (java的关键字都是小写的!!)
保留字:const goto
15.HashMap 和 Hashtable 有什么区别?
hashMap去掉了HashTable 的contains方法,但是加上了containsValue()和containsKey()方法。
hashTable同步的,而HashMap是非同步的,效率上比hashTable要高。
hashMap允许空键值,而hashTable不允许。
16.如何决定使用 HashMap 还是 TreeMap?
对于在Map中插入、删除和定位元素这类操作,HashMap是最好的选择。
假如需要对一个有序的key集合进行遍历,TreeMap是更好的选择。
17.说一下 HashMap 的实现原理?
HashMap概述: HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键,无序
HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。
当我们往Hashmap中put元素时,首先根据key的hashcode重新计算hash值,根绝hash值得到这个元素在数组中的位置(下标),如果该数组在该位置上已经存放了其他元素,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放入链尾.如果数组中该位置没有元素,就直接将该元素放到数组的该位置上。
需要注意Jdk 1.8中对HashMap的实现做了优化,当链表中的节点数据超过八个之后,该链表会转为红黑树来提高查询效率,从原来的O(n)到O(logn)
18.说一下HashSet 的实现原理?
HashSet底层由HashMap实现
HashSet的值存放于HashMap的key上
HashMap的value统一为PRESENT
19.ArrayList 和 LinkedList 的区别是什么?
最明显的区别是 ArrrayList底层的数据结构是数组,支持随机访问,
而 LinkedList 的底层数据结构是双向循环链表,不支持随机访问。
使用下标访问一个元素,ArrayList 的时间复杂是 O(1),而 LinkedList 是 O(n)。
20.如何实现数组和 List 之间的转换?
List转换成为数组:调用ArrayList的toArray方法。
数组转换成为List:调用Arrays的asList方法。
21.ArrayList 和 Vector 的区别是什么?
Vector是同步的,而ArrayList不是。然而,如果你寻求在迭代的时候对列表进行改变,你应该使用CopyOnWriteArrayList。
ArrayList比Vector快,它因为有同步,不会过载。
ArrayList更加通用,因为我们可以使用Collections工具类轻易地获取同步列表和只读列表。
22.Array 和 ArrayList 有何区别?
Array可以容纳基本类型和对象,而ArrayList只能容纳对象。
Array是指定大小的,而ArrayList大小是固定的。
Array没有提供ArrayList那么多功能,比如addAll、removeAll和iterator等。
23.在 Queue 中 poll()和 remove()有什么区别?
poll() 和 remove() 都是从队列中取出一个元素,但是 poll() 在获取元素失败的时候会返回空,但是 remove() 失败的时候会抛出异常。
24.哪些集合类是线程安全的?
vector
:就比arraylist多了个同步化机制(线程安全),因为效率较低,现在已经不太建议使用.
enumeration
:枚举,相当于迭代器。
statck
:堆栈类,先进后出。(hashtable的子类)
hashtable
:就比hashmap多了个线程安全。
\31. 迭代器 Iterator 是什么?
迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。
\32. Iterator 怎么使用?有什么特点?
Java中的Iterator功能比较简单,并且只能单向移动:
(1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。
(2) 使用next()获得序列中的下一个元素。
(3) 使用hasNext()检查序列中是否还有元素。
(4) 使用remove()将迭代器新返回的元素删除。
Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。
\33. Iterator 和 ListIterator 有什么区别?
Iterator可用来遍历Set和List集合,但是ListIterator只能用来遍历List。
Iterator对集合只能是前向遍历,ListIterator既可以前向也可以后向。
ListIterator实现了Iterator接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等。
8.ArrayList和Vector的区别。
这两个类都实现了List接口(List接口继承了Collection接口),他们都是有序集合,数据是允许重复的.
ArrayList与Vector的区别,这主要包括两个方面。
数据增长:
ArrayList与Vector都有一个初始的容量大小,当存储进它们里面的元素的个数超过了容量时,就需要增加ArrayList与Vector的存储空间,每次增加多个存储单元,增加的存储单元的个数在内存空间利用与程序效率之间要取得一定的平衡。
Vector默认增长为原来两倍,而ArrayList的增长策略在文档中没有明确规定(从源代码看到的是增长为原来的1.5倍)。ArrayList与Vector都可以设置初始的空间大小,Vector还可以设置增长的空间大小,而ArrayList没有提供设置增长空间的方法。
同步性:
Vector是线程安全的,而ArrayList是线程序不安全的
对于Vector&ArrayList、Hashtable&HashMap,要记住线程安全的问题,记住Vector与Hashtable是旧的,是java一诞生就提供了的,它们是线程安全的,ArrayList与HashMap是java2时才提供的,它们是线程不安全的。
9.能创建 volatile 数组吗?
可以,但是创建的对象或数组的地址具有可见性,里面的数据是不可见的。
10.去掉一个Vector集合中重复的元素。
方法一:HashSet set = new HashSet(vector);
方法二:
Vector newVector = new Vector();
for (int i=0;i<vector.size();i++)
{
Object obj = vector.get(i);
if(!newVector.contains(obj); //不包含
newVector.add(obj);
}
11.说出ArrayList,Vector, LinkedList的存储性能和特性。
ArrayList和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢。
Vector由于使用了synchronized方法(线程安全),通常性能上较ArrayList差。
而LinkedList使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。LinkedList也是线程不安全的,LinkedList提供了一些方法,使得LinkedList可以被当作堆栈和队列来使用。
13.Set里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用==还是equals()? 它们有何区别?
Set里的元素是不能重复的,那么用iterator()方法来区分重复与否。equals()是判读两个Set是否相等。
Set里的元素是不能重复的,元素重复与否是使用equals()方法进行判断的。
equals()和==方法决定引用值是否指向同一对象,equals()在类中被覆盖,为的是当两个分离的对象的内容和类型相配的话,返回真值。
14.两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对?
对。
如果对象要保存在HashSet或HashMap中,它们的equals相等,那么,它们的hashcode值就必须相等。
如果不是要保存在HashSet或HashMap,则与hashcode没有什么关系了,这时候hashcode不等是可以的。
例如arrayList存储的对象就不用实现hashcode,当然,我们没有理由不实现,通常都会去实现的。
15.TreeSet里面放对象,如果同时放入了父类和子类的实例对象,那比较时使用的是父类的compareTo方法,还是使用的子类的compareTo方法,还是抛异常!
当前的add方法放入的是哪个对象,就调用哪个对象的compareTo方法,至于这个compareTo方法怎么做,就看当前这个对象的类中是如何编写这个方法的。
实验代码
public class Parent implements Comparable {
private int age = 0;
public Parent(int age){
this.age = age;
}
public int compareTo(Object o) {
System.out.println(“method of parent”);
Parent o1 = (Parent)o;
return age>o1.age?1:age<o1.age?-1:0;
}
}
public class Child extends Parent {
public Child(){
super(3);
}
public int compareTo(Object o) {
System.out.println(“method of child”);
return 1;
}
}
public class TreeSetTest {
public static void main(String[] args) {
TreeSet set = new TreeSet();
set.add(new Parent(3));
set.add(new Child());
set.add(new Parent(4));
System.out.println(set.size());
}
}
16.快速失败(fail-.fast)和安全失败(fail-.safe)的区别是什么?
Iterator的安全失败是基于对底层集合做拷贝,因此,它不受源集合上修改的影响。
java.util 包下面的所有的集合类都是快速失败的,而java.util.concurrent包下面的所有的类都是安全失败的。
快速失败的迭代器会抛出ConcurrentModificationException异常,而安全失败的迭代器永远不会抛出这样的异常。
20.HashMap什么时候进行扩容呢?
HashMap的一些重要的特性是它的容量 (capacity),负载因子(load factor)和扩容极限(threshold resizing)。
当HshMap中的元素个数超过数组大小*loadFactor时,就会进行数组扩容,loadFactor的默认值为0.75, 也就是说,默认情况下,数组大小为16,那么当hashmap中元素个数超过16*0.75=12的时候,就把数组的大小扩展为2*16=32,即扩大一倍, 然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知hashmap中元素的个数,那么预设元素的个数能够有效的提高hashmap的性能。
22.CorrentHashMap的工作原理?
在jdk 8中,ConcurrentHashMap不再使用Segment分离锁,而是采用一种乐观锁CAS算法来实现同步问题,但其底层还是“数组+链表->红黑树”的实现。
HashMap的底层实现,之后会问ConcurrentHashMap的底层实现
HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。允许使用null值和null键。
ConcurrentHashMap基于双数组和链表的Map接口的同步实现
ConcurrentHashMap中元素的key是唯一的、value值可重复
ConcurrentHashMap不允许使用null值和null键
ConcurrentHashMap是无序的
23.LinkedHashMap的实现原理?
LinkedHashMap也是基于HashMap实现的
LinkedHashMap通过继承hashMap中的Entry,并添加两个属性Entry before,after,和header结合起来组成一个双向链表,来实现按插入顺序或访问顺序排序。
26.为什么集合类没有实现Cloneable和Serializable接口?
克隆(cloning)或者是序列化(serialization)的语义和含义是跟具体的实现相关的。
因此,应该 由集合类的具体实现来决定如何被克隆或者是序列化。
多线程
\35. 并行和并发有什么区别?
并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔发生。
并行是在不同实体上的多个事件,并发是在同一实体上的多个事件。
在一台处理器上“同时”处理多个任务,在多台处理器上同时处理多个任务。如hadoop分布式集群。
所以并发编程的目标是充分的利用处理器的每一个核,以达到最高的处理性能。
\36. 线程和进程的区别?
简而言之,进程是程序运行和资源分配的基本单位,一个程序至少有一个进程,一个进程至少有一个线程。
进程在执行过程中拥有独立的内存单元,而多个线程共享内存资源,减少切换次数,从而效率更高。
线程是进程的一个实体,是cpu调度和分派的基本单位,是比程序更小的能独立运行的基本单位。
同一进程中的多个线程之间可以并发执行。
37.守护线程是什么?
守护线程(即daemon thread),是个服务线程,准确地来说就是服务其他的线程。
\38. 创建线程有哪几种方式?
①. 继承Thread类创建线程类
定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。
创建Thread子类的实例,即创建了线程对象。
调用线程对象的start()方法来启动该线程。
②. 通过Runnable接口创建线程类
定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
调用线程对象的start()方法来启动该线程。
③. 通过Callable和Future创建线程
创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值
使用FutureTask对象作为Thread对象的target创建并启动新线程。
调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。
\39. 说一下 runnable 和 callable 有什么区别?
Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;
Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。
\41. sleep() 和 wait() 有什么区别?
sleep():方法是线程类(Thread)的静态方法,让调用线程进入睡眠状态,让出执行机会给其他线程,等到休眠时间结束后,线程进入就绪状态和其他线程一起竞争cpu的执行时间。因为sleep() 是static静态的方法,他不能改变对象的机锁,当一个synchronized块中调用了sleep() 方法,线程虽然进入休眠,但是对象的机锁没有被释放,其他线程依然无法访问这个对象。
wait():wait()是Object类的方法,当一个线程执行到wait方法时,它就进入到一个和该对象相关的等待池,同时释放对象的机锁,使得其他线程能够访问,可以通过notify,notifyAll方法来唤醒等待的线程
\42. notify()和 notifyAll()有什么区别?
如果线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。
当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify后只要一个线程会由等待池进入锁池,而notifyAll会将该对象等待池内的所有线程移动到锁池中,等待锁竞争。
优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。
\43. 线程的 run()和 start()有什么区别?
每个线程都是通过某个特定Thread对象所对应的方法run()来完成其操作的,方法run()称为线程体。通过调用Thread类的start()方法来启动一个线程。
start()方法来启动一个线程,真正实现了多线程运行。这时无需等待run方法体代码执行完毕,可以直接继续执行下面的代码; 这时此线程是处于就绪状态, 并没有运行。 然后通过此Thread类调用方法run()来完成其运行状态, 这里方法run()称为线程体,它包含了要执行的这个线程的内容, Run方法运行结束, 此线程终止。然后CPU再调度其它线程。
run()方法是在本线程里的,只是线程里的一个函数,而不是多线程的。 如果直接调用run(),其实就相当于是调用了一个普通函数而已,直接待用run()方法必须等待run()方法执行完毕才能执行下面的代码,所以执行路径还是只有一条,根本就没有线程的特征,所以在多线程执行时要使用start()方法而不是run()方法。
\44. 创建线程池有哪几种方式?
①. newFixedThreadPool(int nThreads)
创建一个固定长度的线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程规模将不再变化,当线程发生未预期的错误而结束时,线程池会补充一个新的线程。
②. newCachedThreadPool()
创建一个可缓存的线程池,如果线程池的规模超过了处理需求,将自动回收空闲线程,而当需求增加时,则可以自动添加新线程,线程池的规模不存在任何限制。
③. newSingleThreadExecutor()
这是一个单线程的Executor,它创建单个工作线程来执行任务,如果这个线程异常结束,会创建一个新的来替代它;它的特点是能确保依照任务在队列中的顺序来串行执行。
④. newScheduledThreadPool(int corePoolSize)
创建了一个固定长度的线程池,而且以延迟或定时的方式来执行任务,类似于Timer。
\45. 线程池都有哪些状态?
线程池有5种状态:Running
、ShutDown
、Stop
、Tidying
、Terminated
。
\46. 线程池中 submit()和 execute()方法有什么区别?
接收的参数不一样
submit有返回值,而execute没有
submit方便Exception处理
\47. 在 java 程序中怎么保证多线程的运行安全?
线程安全在三个方面体现:
原子性:提供互斥访问,同一时刻只能有一个线程对数据进行操作,(atomic,synchronized);
可见性:一个线程对主内存的修改可以及时地被其他线程看到,(synchronized,volatile);
有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序,该观察结果一般杂乱无序,(happens-before原则)。
\48. 多线程锁的升级原理是什么?
在Java中,锁共有4种状态,级别从低到高依次为:无状态锁,偏向锁,轻量级锁和重量级锁状态,这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级。
\49. 什么是死锁?
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。是操作系统层面的一个错误,是进程死锁的简称.
\50. 怎么防止死锁?
死锁的四个必要条件:
互斥条件:进程对所分配到的资源不允许其他进程进行访问,若其他进程访问该资源,只能等待,直至占有该资源的进程使用完成后释放该资源
请求和保持条件:进程获得一定的资源之后,又对其他资源发出请求,但是该资源可能被其他进程占有,此事请求阻塞,但又对自己获得的资源保持不放
不可剥夺条件:是指进程已获得的资源,在未完成使用之前,不可被剥夺,只能在使用完后自己释放
环路等待条件:是指进程发生死锁后,若干进程之间形成一种头尾相接的循环等待资源关系
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
\51. ThreadLocal 是什么?有哪些使用场景?
线程局部变量是局限于线程内部的变量,属于线程自身所有,不在多个线程间共享。
Java提供ThreadLocal类来支持线程局部变量,是一种实现线程安全的方式。
但是在管理环境下(如 web 服务器)使用线程局部变量的时候要特别小心,在这种情况下,工作线程的生命周期比任何应用变量的生命周期都要长。任何线程局部变量一旦在工作完成后没有释放,Java 应用就存在内存泄露的风险。
synchronized 底层实现原理?
synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性。
Java中每一个对象都可以作为锁,这是synchronized实现同步的基础:
普通同步方法,锁是当前实例对象
静态同步方法,锁是当前类的class对象
同步方法块,锁是括号里面的对象
\53. synchronized 和 volatile 的区别是什么?
volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;
synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的。
volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性。
volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化。
\54. synchronized 和 Lock 有什么区别?
首先synchronized是java内置关键字,在jvm层面,Lock是个java类;
synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;
synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),
Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;
用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;
synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可);
Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。
\55. synchronized 和 ReentrantLock 区别是什么?
synchronized是和if、else、for、while一样的关键字,
ReentrantLock是类,这是二者的本质区别。既然ReentrantLock是类,那么它就提供了比synchronized更多更灵活的特性,可以被继承、可以有方法、可以有各种各样的类变量,ReentrantLock比synchronized的扩展性体现在几点上:
ReentrantLock可以对获取锁的等待时间进行设置,这样就避免了死锁
ReentrantLock可以获取各种锁的信息
ReentrantLock可以灵活地实现多路通知
另外,二者的锁机制其实也是不一样的:ReentrantLock底层调用的是Unsafe的park方法加锁,synchronized操作的应该是对象头中markword。
\56. atomic 的原理?
Atomic包中的类基本的特性就是在多线程环境下,当有多个线程同时对单个(包括基本类型及引用类型)变量进行操作时,具有排他性,即当多个线程同时对该变量的值进行更新时,仅有一个线程能成功,而未成功的线程可以向自旋锁一样,继续尝试,一直等到执行成功。
Atomic系列的类中的核心方法都会调用unsafe类中的几个本地方法。
我们需要先知道一个东西就是Unsafe类,全名为:sun.misc.Unsafe
,这个类包含了大量的对C代码的操作,包括很多直接内存分配以及原子操作的调用,而它之所以标记为非安全的,是告诉你这个里面大量的方法调用都会存在安全隐患,需要小心使用,否则会导致严重的后果,例如在通过unsafe分配内存的时候,如果自己指定某些区域可能会导致一些类似C++一样的指针越界到其他进程的问题。
反射
\57. 什么是反射?
反射主要是指程序可以访问、检测和修改它本身状态或行为的一种能力
Java反射:
在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法。对于任意一个对象,能否调用它的任意一个方法
Java反射机制主要提供了以下功能:
在运行时构造任意一个类的对象。
在运行时判断任意一个对象所属的类。
在运行时判断任意一个类所具有的成员变量和方法。
在运行时调用任意一个对象的方法。
\58. 什么是 java 序列化?什么情况下需要序列化?
简单说就是为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出来。虽然你可以用你自己的各种各样的方法来保存object states,但是Java给你提供一种应该比你自己好的保存对象状态的机制,那就是序列化。
什么情况下需要序列化:
a)当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;
b)当你想用套接字在网络上传送对象的时候;
c)当你想通过RMI传输对象的时候;
\59. 动态代理是什么?有哪些应用?
动态代理:
当想要给实现了某个接口的类中的方法,加一些额外的处理。比如说加日志,加事务等。可以给这个类创建一个代理,故名思议就是创建一个新的类,这个类不仅包含原来类方法的功能,而且还在原来的基础上添加了额外处理的新类。这个代理类并不是定义好的,是动态生成的。具有解耦意义,灵活,扩展性强。
动态代理的应用:
Spring的AOP, 加事务, 加权限, 加日志
\60. 怎么实现动态代理?
首先必须定义一个接口,还要有一个InvocationHandler
(将实现接口的类的对象传递给它)处理类。再有一个工具类Proxy(习惯性将其称为代理类,因为调用他的newInstance()可以产生代理对象,其实他只是一个产生代理对象的工具类)。利用到InvocationHandler,拼接代理类源码,将其编译生成代理类的二进制码,利用加载器加载,并将其实例化产生代理对象,最后返回。
面向对象设计原则
1.单一职责原则
- 在面向对象设计中,分工理论就是单一职责原则(Single Pesponsibility Prineiple, SRP)
- 两个含义
- 避免相同的职责分散到不同的类中
- 避免一个类承担太多职责
- 为什么要遵循单一设计原则
- 可以减少类之间的耦合:当需求变化时,只修改一个类,从而隔离了变化。
- 提高类的复用性
- 单一职责使得组件可以方便的拆卸和组装
- 应用:用工厂模式来实现不同数据库操作类。
2.接口隔离原则:
- 接口隔离原则(Interface Segregation Principle, ISP): 客户端不应该被强迫实现不会使用的接口
- 接口隔离原则的主要观点
- 一个类对另外一个类的依赖性应当是建立在最小的接口上
- ISP 可以达到不强迫客户依赖于他们不使用的方法,接口的实现类应该只呈现为单一职责的角色(遵守SRP原则)。
- ISP 还可降低客户端之间的相互影响——当某个客户程序要求提供新的职责(需求变更)而迫使接口发生改变时,影响到其他客户程序的可能性会是最小的。
- 客户端程序不应该依赖它不需要的接口方法
- ISP强调的是接口对客户端的承诺越少越好,并且要做到专一。
- 接口污染:过于臃肿的接口设计是对接口的污染。接口污染就是为接口添加不必要的职责,如果开发人员在接口中增加一个新功能的主要目的只是减少接口实现类的数目,则此设计导致接口被不断的“污染” 并 “变胖”
3.开放 - 封闭原则:
- 开放:模块的行为必须是开放的、支持扩展的,而不是僵化的。
- 关闭:在模块的功能进行扩展时,不应该影响或大规模影响已有的程序模块。
- 一个模块在扩展性方面应该是开放的,在更改性方面应该是封闭的。
- 该原则的核心是想是对抽象编程,而不是具体编程,因为抽象相对稳定。
让类依赖于固定的抽象,这样的修改就是封闭的,通过面向对象的继承和多态机制,可以实现对抽象体的继承,
通过覆写其方法改变固有行为,实现新的扩展方法,所以对于扩展就是开放的。
- 在设计方面充分应用 抽象 和 封装 的思想。
- 在系统功能编程实现方面应用面向接口的编程。
4.替换原则 :
里氏替换原则(Liskov Substiution Principle, LSP)定义以及主要思想:子类型必须能够替换掉它们的父类型、并出现在父类能够出现的任何地方。
- 父类的方法都要在子类中实现或者重写, 派生类只实现其抽象类中声明的方法, 而不应当给出多余的方法定义或实现。
- 在客户端程序中只应该出现父类对象,而不是直接使用子类对象, 这样可以实现运行期绑定(多态绑定)。
5.依赖倒置原则:
依赖倒置的核心原则是解耦,如果脱离这个最原始的原则,那就是本末倒置。
- 将依赖关系倒置为依赖接口:
- 上层模块不应该依赖下层模块, 它们共同依赖于一个抽象。
- 抽象不能依赖于具体, 具体应该要依赖于抽象
- IOC(Inversion of Control) 是依赖倒置原则(Dependence Inversion Principle, DIP)的同义词。
- DI: 依赖注入
- DS: 依赖查找
- 如何满足DIP:
- 每个较高层次类都为它所需要的服务提出一个接口声明, 较低层次实现这个接口
- 每个高层类都通过该抽象接口使用服务