1、面向对象特征:封装、继承、多态
封装:把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口来访问。面向对象的本质就是将现实世界描绘成一系列完全自治、封闭的对象。我们在类中编写的方法就是对现实细节的一种封装;我们编写一个类就是对数据和数据操作的封装。封装就是隐藏一切可隐藏的东西,只像外面提供简单的编程接口。 继承:是从已有的类得到继承信息创建新类的过程,被继承类称为父类,继承类称为子类。继承让变化中的系统有了一定的延续性,同时继承也是封装程序中可变因素的重要手段。 多态:允许不同子类型的对象对同一消息作出不同的响应。方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式。
(1)方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载(Overloading)。 (2)方法重写是在子类存在方法与父类的方法的名字相同,要使用super关键字,而且参数的个数与类型一样,返回值也一样的方法,就称为重写(Overriding)。 (3)方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。
2、修饰符总结(public、private、protected)+不写时候的默认
不写时候的默认:对于同一个包中的其他类相当于公开(public),对于不是同一个包中的其他类相当于私有(private)。受保护(protected)对子类相当于公开,对不是同一包中的没有父子关系的类相当于私有。Java中,外部类的修饰符只能是public或默认
3、String 是最基本的数据类型吗?
不是,Java的基础数据类型为8个: 其中四个为整数类型(byte【8位】、short【16位】、int【32位】、long【64位】),注意事项:int i=5; // 5 叫直接量(或字面量),即 直接写出的常数。整数字面量默认都为 int 类型,所以在定义的 long 型数据后面加 L或 l。小于 32 位数的变量,都按 int 结果计算 两个浮点数类型(float、double):float:32 位,后缀 F 或 f,1位符号位,8 位指数,23 位有效尾数。double:64 位,最常用,后缀 D 或 d,1 位符号位,11 位指数,52 位有效尾 一个字符类型(char):char:16 位,是整数类型,用单引号括起来的 1 个字符(可以是一个中文字符),使用 Unicode 码代表字符,0~2^16-1(65535) 一个布尔类型(boolean):true 真 和 false 假 记忆:8位:Byte(字节型) 16位:short(短整型)、char(字符型)32位:int(整型)、float(单精度型/浮点型)64位:long(长整型)、double(双精度型)最后一个:boolean(布尔类型)
4、float f=3.4;是否正确?
不对,3.4是双精度数,将双精度型(double)赋值给浮点型(float)属于下转型,会造成精度损失,因此需要强制类型转换float f =(float)3.4; 或者写成float f =3.4F 类型转换:char-->自动转换:byte-->short-->int-->long-->float-->double 强制转换:①会损失精度,产生误差,小数点以后的数字全部舍弃。②容易超过取值范围。
5、short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗?
前者是错误的,1是int类型,那么short s1 = 1; s1 = s1 + 1等同于1+1=2(int 非 short类型),后者是正确的,s1+=1等同于s1=(short)(s1+1),因此类型是相同的,所以正确
6、Java有没有goto?
有,但不适用,被称为保留字
7、int和Integer有什么区别?
Java 为每个原始类型提供了包装类型,Java 5开始引入了自动装箱/拆箱机制,使得二者可以相互转换。 - 原始类型: boolean,char,byte,short,int,long,float,double - 包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double 映射:对象比较&&装箱、拆箱
例子1:
class AutoUnboxingTest { public static void main(String[] args) { Integer a = new Integer(3); Integer b = 3; // 将3自动装箱成Integer类型 int c = 3; System.out.println(a == b); // false 两个引用没有引用同一对象 System.out.println(a == c); // true a自动拆箱成int类型再和c比较 } }
例子2:
public class Test03 { public static void main(String[] args) { Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150; System.out.println(f1 == f2); System.out.println(f3 == f4); } }
整型字面量的值在-128到127之间,那么不会new新的Integer对象,而是直接引用常量池中的Integer对象,所以f1==f2的结果是true,而f3==f4的结果是false。
8、&和&&的区别?
&运算符有两种用法:(1)按位与;(2)逻辑与。&&运算符是短路与运算。逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true整个表达式的值才是true。&&之所以称为短路运算是因为,如果&&左边的表达式的值是false,右边的表达式会被直接短路掉,不会进行运算。很多时候我们可能都需要用&&而不是&,例如在验证用户登录时判定用户名不是null而且不是空字符串,应当写为:username != null &&!username.equals(""),二者的顺序不能交换,更不能用&运算符,因为第一个条件如果不成立,根本不能进行字符串的equals比较,否则会产生NullPointerException异常。注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此。
9、hashCode()&&equals()
两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对? 答:不对,如果两个对象x和y满足x.equals(y) == true,它们的哈希码(hash code)应当相同。Java对于eqauls方法和hashCode方法是这样规定的:(1)如果两个对象相同(equals方法返回true),那么它们的hashCode值一定要相同;(2)如果两个对象的hashCode相同,它们并不一定相同。当然,你未必要按照要求去做,但是如果你违背了上述原则就会发现在使用容器时,相同的对象可以出现在Set集合中,同时增加新元素的效率会大大下降(对于使用哈希存储的系统,如果哈希码频繁的冲突将会造成存取性能急剧下降)。
10、解释内存中的栈(stack)、堆(heap)和静态区(static area)的用法。
答:通常我们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的现场保存都使用内存中的栈空间;而通过new关键字和构造器创建的对象放在堆空间;程序中的字面量(literal)如直接书写的100、"hello"和常量都是放在静态区中。栈空间操作起来最快但是栈很小,通常大量的对象都是放在堆空间,理论上整个内存没有被其他进程使用的空间甚至硬盘上的虚拟内存都可以被当成堆空间来使用。
String str = new String("hello");
上面的语句中变量str放在栈上,用new创建出来的字符串对象放在堆上,而"hello"这个字面量放在静态区。
11、Math.round(11.5) 等于多少?Math.round(-11.5)等于多少?
Math.round(11.5)的返回值是12,Math.round(-11.5)的返回值是-11。四舍五入的原理是在参数上加0.5然后进行下取整。
12、用最有效率的方法计算2乘以8?
2 << 3(左移3位相当于乘以2的3次方,右移3位相当于除以2的3次方)
13、数组有没有length()方法?String有没有length()方法?
数组没有length()方法,有length 的属性。String 有length()方法
14、在Java中,如何跳出当前的多重嵌套循环?
在最外层循环前加一个标记如A,然后用break A;可以跳出多重循环
15、构造器(constructor)是否可被重写(override)?
构造器不能被继承,因此不能被重写,但可以被重载。
16、是否可以继承String类?
String 类是final类,不可以被继承。
17、当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?
是值传递。Java语言的方法调用只支持参数的值传递
18、String和StringBuilder、StringBuffer的区别?
String是只读字符串,也就意味着String引用的字符串内容是不能被改变的。而StringBuffer/StringBuilder类表示的字符串对象可以直接进行修改。StringBuilder是Java 5中引入的,它和StringBuffer的方法完全相同,区别在于它是在单线程环境下使用的,因为它的所有方面都没有被synchronized修饰,因此它的效率也比StringBuffer要高。
19、JVM加载class文件的原理机制?
JVM中类的装载是由类加载器(ClassLoader)和它的子类来实现的,Java中的类加载器是一个重要的Java运行时系统组件,它负责在运行时查找和装入类文件中的类。
20、char 型变量中能不能存贮一个中文汉字,为什么?
char类型可以存储一个中文汉字,因为Java中使用的编码是Unicode(不选择任何特定的编码,直接使用字符在字符集中的编号,这是统一的唯一方法),一个char类型占2个字节(16比特),所以放一个中文是没问题
21、抽象类(abstract class)和接口(interface)有什么不同?
抽象类和接口都不能够实例化,但可以定义抽象类和接口类型的引用。一个类如果继承了某个抽象类或者实现了某个接口都需要对其中的抽象方法全部进行实现,否则该类仍然需要被声明为抽象类。接口比抽象类更加抽象,因为抽象类中可以定义构造器,可以有抽象方法和具体方法,而接口中不能定义构造器而且其中的方法全部都是抽象方法。抽象类中的成员可以是private、默认、protected、public的,而接口中的成员全都是public的。抽象类中可以定义成员变量,而接口中定义的成员变量实际上都是常量。有抽象方法的类必须被声明为抽象类,而抽象类未必要有抽象方法。
22、静态嵌套类(Static Nested Class)和内部类(Inner Class)的不同?
Static Nested Class是被声明为静态(static)的内部类,它可以不依赖于外部类实例被实例化。而通常的内部类需要在外部类实例化后才能实例化
23、Java 中会存在内存泄漏吗,请简单描述
理论上Java因为有垃圾回收机制(GC)不会存在内存泄露问题(这也是Java被广泛使用于服务器端编程的一个重要原因);然而在实际开发中,可能会存在无用但可达的对象,这些对象不能被GC回收,因此也会导致内存泄露的发生。
24、抽象的(abstract)方法是否可同时是静态的(static),是否可同时是本地方法(native),是否可同时被synchronized修饰?
都不能。抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的。本地方法是由本地代码(如C代码)实现的方法,而抽象方法是没有实现的,也是矛盾的。synchronized和方法的实现细节有关,抽象方法不涉及实现细节,因此也是相互矛盾的。
25、静态变量和实例变量的区别
静态变量是被static修饰符修饰的变量,也称为类变量,它属于类,不属于类的任何一个对象,一个类不管创建多少个对象,静态变量在内存中有且仅有一个拷贝;实例变量必须依存于某一实例,需要先创建对象然后通过对象才能访问到它。静态变量可以实现让多个对象共享内存。
26、是否可以从一个静态(static)方法内部发出对非静态(non-static)方法的调用?
不可以,静态方法只能访问静态成员,因为非静态方法的调用要先创建对象,在调用静态方法时可能对象并没有被初始化。
27、如何实现对象克隆?
有两种方式: 1). 实现Cloneable接口并重写Object类中的clone()方法; 2). 实现Serializable接口,通过对象的序列化和反序列化实现克隆
28、GC是什么?为什么要有GC?
垃圾收集的意思,可以自动监测对象是否超过作用域从而达到自动回收内存的目的
29、String s = new String("xyz");创建了几个字符串对象?
两个对象,一个是静态区的"xyz",一个是用new创建在堆上的对象。
语句中变量s放在栈上,用new创建出来的字符串对象放在堆上,而"xyz"这个字面量放在静态区。
30、接口是否可继承(extends)接口?抽象类是否可实现(implements)接口?抽象类是否可继承具体类(concrete class)?
接口可以继承接口,而且支持多重继承。抽象类可以实现(implements)接口,抽象类可继承具体类也可以继承抽象类。
31、一个".java"源文件中是否可以包含多个类(不是内部类)?有什么限制?
可以,但一个源文件中最多只能有一个公开类(public class)而且文件名必须和公开类的类名完全保持一致。
32、Anonymous Inner Class(匿名内部类)是否可以继承其它类?是否可以实现接口?
可以继承其他类或实现其他接口,在Swing编程和Android开发中常用此方式来实现事件监听和回调。
33、内部类可以引用它的包含类(外部类)的成员吗?有没有什么限制?
内部类对象可以访问创建它的外部类对象的成员,包括私有成员。
34、Java 中的final关键字有哪些用法?
(1)修饰类:表示该类不能被继承;(2)修饰方法:表示方法不能被重写;(3)修饰变量:表示变量只能一次赋值以后值不能被修改(常量)。
35、数据类型之间的转换:
- 如何将字符串转换为基本数据类型?
- 如何将基本数据类型转换为字符串?
- 调用基本数据类型对应的包装类中的方法parseXXX(String)或valueOf(String)即可返回相应基本类型; - 一种方法是将基本数据类型与空字符串("")连接(+)即可获得其所对应的字符串;另一种方法是调用String 类中的valueOf()方法返回相应字符串
36、如何实现字符串的反转及替换?
可用字符串构造一 StringBuffer 对象,然后调用 StringBuffer 中的 reverse 方法即可实现字符串的反转,调用 replace 方法即可实现字符串的替换。
public class test { public static void main(String[] args) { StringBuffer sb=new StringBuffer("hello"); System.out.println(sb); sb.reverse(); System.out.println(sb); } }
public static void main(String[] args) { StringBuffer buf = new StringBuffer() ; // 声明StringBuffer对象 buf.append("Hello ").append("World!!") ; // 向StringBuffer添加内容 buf.replace(6,11,"LiXingHua") ; // 将world的内容替换 System.out.println("内容替换之后的结果:" + buf) ; // 输出内容 }
37、怎样将GB2312编码的字符串转换为ISO-8859-1编码的字符串?
String s1 = "你好"; String s2 = new String(s1.getBytes("GB2312"), "ISO-8859-1");
38、日期和时间:
- 如何取得年月日、小时分钟秒?
- 如何取得从1970年1月1日0时0分0秒到现在的毫秒数?
- 如何取得某月的最后一天?
- 如何格式化日期?
问题1:创建java.util.Calendar 实例,调用其get()方法传入不同的参数即可获得参数所对应的值。Java 8中可以使用java.time.LocalDateTimel来获取,代码如下所示。
public class DateTimeTest { public static void main(String[] args) { Calendar cal = Calendar.getInstance(); System.out.println(cal.get(Calendar.YEAR)); System.out.println(cal.get(Calendar.MONTH)); // 0 - 11 System.out.println(cal.get(Calendar.DATE)); System.out.println(cal.get(Calendar.HOUR_OF_DAY)); System.out.println(cal.get(Calendar.MINUTE)); System.out.println(cal.get(Calendar.SECOND)); // Java 8 LocalDateTime dt = LocalDateTime.now(); System.out.println(dt.getYear()); System.out.println(dt.getMonthValue()); // 1 - 12 System.out.println(dt.getDayOfMonth()); System.out.println(dt.getHour()); System.out.println(dt.getMinute()); System.out.println(dt.getSecond()); } }
问题2:以下方法均可获得该毫秒数。
Calendar.getInstance().getTimeInMillis(); System.currentTimeMillis(); Clock.systemDefaultZone().millis(); // Java 8
问题3:代码如下所示。
Calendar time = Calendar.getInstance(); time.getActualMaximum(Calendar.DAY_OF_MONTH);
问题4:利用java.text.DataFormat 的子类(如SimpleDateFormat类)中的format(Date)方法可将日期格式化。Java 8中可以用java.time.format.DateTimeFormatter来格式化时间日期,代码如下所示。
import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.Date; class DateFormatTest { public static void main(String[] args) { SimpleDateFormat oldFormatter = new SimpleDateFormat("yyyy/MM/dd"); Date date1 = new Date(); System.out.println(oldFormatter.format(date1)); // Java 8 DateTimeFormatter newFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd"); LocalDate date2 = LocalDate.now(); System.out.println(date2.format(newFormatter)); }
39、打印昨天的当前时刻。
import java.time.LocalDateTime; class YesterdayCurrent { public static void main(String[] args) { LocalDateTime today = LocalDateTime.now(); LocalDateTime yesterday = today.minusDays(1); System.out.println(yesterday); } }
40、什么时候用断言(assert)?
断言用于保证程序最基本、关键的正确性,通常在开发和测试时开启。
41、Error和Exception有什么区别?
Error表示系统级的错误和程序不必处理的异常,是恢复不是不可能但很困难的情况下的一种严重问题;比如内存溢出,不可能指望程序能处理这样的情况;Exception表示需要捕捉或者需要程序进行处理的异常,是一种设计或实现问题;也就是说,它表示如果程序运行正常,从不会发生的情况。
42、try{}里有一个return语句,那么紧跟在这个try后的finally{}里的代码会不会被执行,什么时候被执行,在return前还是后?
会执行,在方法返回调用者前执行。
43、Java语言如何进行异常处理,关键字:throws、throw、try、catch、finally分别如何使用?
Java通过面向对象的方法进行异常处理,把各种不同的异常进行分类,并提供了良好的接口。在Java中,每个异常都是一个对象,它是Throwable类或其子类的实例。当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并可以对其进行处理。Java的异常处理是通过5个关键词来实现的:try、catch、throw、throws和finally。一般情况下是用try来执行一段程序,如果系统会抛出(throw)一个异常对象,可以通过它的类型来捕获(catch)它,或通过总是执行代码块(finally)来处理;try用来指定一块预防所有异常的程序;catch子句紧跟在try块后面,用来指定你想要捕获的异常的类型;throw语句用来明确地抛出一个异常;throws用来声明一个方法可能抛出的各种异常(当然声明异常时允许无病呻吟);finally为确保一段代码不管发生什么异常状况都要被执行;try语句可以嵌套,每当遇到一个try语句,异常的结构就会被放入异常栈中,直到所有的try语句都完成。如果下一级的try语句没有对某种异常进行处理,异常栈就会执行出栈操作,直到遇到有处理这种异常的try语句或者最终将异常抛给JVM。
44、列出一些你常见的运行时异常?
- ArithmeticException(算术异常) - ClassCastException (类转换异常) - IllegalArgumentException (非法参数异常) - IndexOutOfBoundsException (下标越界异常) - NullPointerException (空指针异常) - SecurityException (安全异常)
45、List、Set、Map是否继承自Collection接口?
List、Set 是,Map 不是。Map是键值对映射容器,与List和Set有明显的区别,而Set存储的零散的元素且不允许有重复元素(数学中的集合也是如此),List是线性结构的容器,适用于按数值索引访问元素的情形。
46、ArrayList、Vector、LinkedList的存储性能和特性。
LinkedList插入速度较快,但是由于ArrayList和LinkedListed都是非线程安全的,如果遇到多个线程操作同一个容器的场景,则可以通过工具类Collections中的synchronizedList方法将其转换成线程安全的容器后再使用
47、Collection和Collections的区别?
Collection是一个接口,它是Set、List等容器的父接口;Collections是个一个工具类,提供了一系列的静态方法来辅助容器操作,这些方法包括对容器的搜索、排序、线程安全化等等。
48、List、Map、Set三个接口存取元素时,各有什么特点?
List以特定索引来存取元素,可以有重复元素。Set不能存放重复元素(用对象的equals()方法来区分元素是否重复)。Map保存键值对(key-value pair)映射,映射关系可以是一对一或多对一。Set和Map容器都有基于哈希存储和排序树的两种实现版本,基于哈希存储的版本理论存取时间复杂度为O(1),而基于排序树版本的实现在插入或删除元素时会按照元素或元素的键(key)构成排序树从而达到排序和去重的效果。
49、TreeMap和TreeSet在排序时如何比较元素?Collections工具类中的sort()方法如何比较元素?
TreeSet要求存放的对象所属的类必须实现Comparable接口,该接口提供了比较元素的compareTo()方法,当插入元素时会回调该方法比较元素的大小。TreeMap要求存放的键值对映射的键必须实现Comparable接口从而根据键对元素进行排序。Collections工具类的sort方法有两种重载的形式,第一种要求传入的待排序容器中存放的对象比较实现Comparable接口以实现元素的比较;第二种不强制性的要求容器中的元素必须可比较,但是要求传入第二个参数,参数是Comparator接口的子类型(需要重写compare方法实现元素的比较),相当于一个临时定义的排序规则,其实就是通过接口注入比较元素大小的算法,也是对回调模式的应用(Java中对函数式编程的支持)。
50、Thread类的sleep()方法和对象的wait()方法都可以让线程暂停执行,它们有什么区别?
sleep()方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会自动恢复。wait()是Object类的方法,调用对象的wait()方法导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的notify()方法(或notifyAll()方法)时才能唤醒等待池中的线程进入等锁池(lock pool),如果线程重新获得对象的锁就可以进入就绪状态。
51、线程的sleep()方法和yield()方法有什么区别?
① sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会; ② 线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态; ③ sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常; ④ sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性。
52、当一个线程进入一个对象的synchronized方法A之后,其它线程是否可进入此对象的synchronized方法B?
不能。其它线程只能访问该对象的非同步方法,同步方法则不能进入。因为非静态方法上的synchronized修饰符要求执行方法时要获得对象的锁,如果已经进入A方法说明对象锁已经被取走,那么试图进入B方法的线程就只能在等锁池(注意不是等待池哦)中等待对象的锁。
53、与线程同步以及线程调度相关的方法
- wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁; - sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常; - notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关; - notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;
54、编写多线程程序有几种实现方式?
Java 5以前实现多线程有两种实现方法:一种是继承Thread类;另一种是实现Runnable接口。两种方式都要通过重写run()方法来定义线程的行为,推荐使用后者,因为Java中的继承是单继承,一个类有一个父类,如果继承了Thread类就无法再继承其他类了,显然使用Runnable接口更为灵活。 Java 5以后创建线程还有第三种方式:实现Callable接口,该接口中的call方法可以在线程执行结束时产生一个返回值
55、synchronized关键字的用法?
synchronized关键字可以将对象或者方法标记为同步,以实现对对象和方法的互斥访问,可以用synchronized(对象) { … }定义同步代码块,或者在声明方法时将synchronized作为方法的修饰符,实现线程锁
56、同步和异步
同步就是指阻塞式操作,而异步就是非阻塞式操作
57、启动一个线程是调用run()还是start()方法?
启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM 调度并执行,这并不意味着线程就会立即运行。run()方法是线程启动后要进行回调(callback)的方法。
58、什么是线程池(thread pool)?
线程池顾名思义就是事先创建若干个可执行的线程放入一个池(容器)中,需要的时候从池中获取线程不用自行创建,使用完毕不需要销毁线程而是放回池中,从而减少创建和销毁线程对象的开销。
4种创建方式:
- newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
- newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
- newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
- newSingleThreadExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。
59、synchronized 和java.util.concurrent.locks.Lock的异同?
Lock是Java 5以后引入的新的API,和关键字synchronized相比主要相同点:Lock 能完成synchronized所实现的所有功能;主要不同点:Lock有比synchronized更精确的线程语义和更好的性能,而且不强制性的要求一定要获得锁。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且最好在finally 块中释放(这是释放外部资源的最好的地方)。
60、Java中有几种类型的流?
字节流和字符流。字节流继承于InputStream、OutputStream,字符流继承于Reader、Writer。在java.io 包中还有许多其他的流,主要是为了提高性能和使用方便。关于Java的I/O需要注意的有两点:一是两种对称性(输入和输出的对称性,字节和字符的对称性);二是两种设计模式(适配器模式和装潢模式)。另外Java中的流不同于C#的是它只有一个维度一个方向。
文件拷贝:
1 import java.io.FileInputStream; 2 import java.io.FileOutputStream; 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.io.OutputStream; 6 import java.nio.ByteBuffer; 7 import java.nio.channels.FileChannel; 8 9 public final class MyUtil { 10 11 private MyUtil() { 12 throw new AssertionError(); 13 } 14 15 public static void fileCopy(String source, String target) throws IOException { 16 try (InputStream in = new FileInputStream(source)) { 17 try (OutputStream out = new FileOutputStream(target)) { 18 byte[] buffer = new byte[4096]; 19 int bytesToRead; 20 while((bytesToRead = in.read(buffer)) != -1) { 21 out.write(buffer, 0, bytesToRead); 22 } 23 } 24 } 25 } 26 }
61、写一个方法,输入一个文件名和一个字符串,统计这个字符串在这个文件中出现的次数。
1 import java.io.BufferedReader; 2 import java.io.FileReader; 3 4 public final class MyUtil { 5 6 // 工具类中的方法都是静态方式访问的因此将构造器私有不允许创建对象(绝对好习惯) 7 private MyUtil() { 8 throw new AssertionError(); 9 } 10 11 /** 12 * 统计给定文件中给定字符串的出现次数 13 * 14 * @param filename 文件名 15 * @param word 字符串 16 * @return 字符串在文件中出现的次数 17 */ 18 public static int countWordInFile(String filename, String word) { 19 int counter = 0; 20 try (FileReader fr = new FileReader(filename)) { 21 try (BufferedReader br = new BufferedReader(fr)) { 22 String line = null; 23 while ((line = br.readLine()) != null) { 24 int index = -1; 25 while (line.length() >= word.length() && (index = line.indexOf(word)) >= 0) { 26 counter++; 27 line = line.substring(index + word.length()); 28 } 29 } 30 } 31 } catch (Exception ex) { 32 ex.printStackTrace(); 33 } 34 return counter; 35 } 36 37 }
62、如何用Java代码列出一个目录下所有的文件?
import java.io.File; class Test12 { public static void main(String[] args) { File f = new File("/Users/Hao/Downloads"); for(File temp : f.listFiles()) { if(temp.isFile()) { System.out.println(temp.getName()); } } } }
63、JDBC操作数据库的步骤
加载驱动。 Class.forName("oracle.jdbc.driver.OracleDriver"); 创建连接。 Connection con = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl", "scott", "tiger"); 创建语句。 PreparedStatement ps = con.prepareStatement("select * from emp where sal between ? and ?"); ps.setInt(1, 1000); ps.setInt(2, 3000); 执行语句。 ResultSet rs = ps.executeQuery(); 处理结果。 while(rs.next()) { System.out.println(rs.getInt("empno") + " - " + rs.getString("ename")); } 关闭资源。 finally { if(con != null) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); } }
64、使用JDBC操作数据库时,如何提升读取数据的性能?如何提升更新数据的性能?
要提升读取数据的性能,可以指定通过结果集(ResultSet)对象的setFetchSize()方法指定每次抓取的记录数(典型的空间换时间策略);要提升更新数据的性能可以使用PreparedStatement语句构建批处理,将若干SQL语句置于一个批处理中执行。
65、事务的ACID是指什么?
- 原子性(Atomic):事务中各项操作,要么全做要么全不做,任何一项操作的失败都会导致整个事务的失败; - 一致性(Consistent):事务结束后系统状态是一致的; - 隔离性(Isolated):并发执行的事务彼此无法看到对方的中间状态; - 持久性(Durable):事务完成后所做的改动都会被持久化,即使发生灾难性的失败。通过日志和同步备份可以在故障发生后重建数据。
补充:关于事务,在面试中被问到的概率是很高的,可以问的问题也是很多的。首先需要知道的是,只有存在并发数据访问时才需要事务。当多个事务访问同一数据时,可能会存在5类问题,包括3类数据读取问题(脏读、不可重复读和幻读)和2类数据更新问题(第1类丢失更新和第2类丢失更新)。
脏读(Dirty Read):A事务读取B事务尚未提交的数据并在此基础上操作,而B事务执行回滚,那么A读取到的数据就是脏数据。
不可重复读(Unrepeatable Read):事务A重新读取前面读取过的数据,发现该数据已经被另一个已提交的事务B修改过了。
幻读(Phantom Read):事务A重新执行一个查询,返回一系列符合查询条件的行,发现其中插入了被事务B提交的行。
第1类丢失更新:事务A撤销时,把已经提交的事务B的更新数据覆盖了。
第2类丢失更新:事务A覆盖事务B已经提交的数据,造成事务B所做的操作丢失。
数据库通常会通过锁机制来解决数据并发访问问题,按锁定对象不同可以分为表级锁和行级锁;按并发事务锁定关系可以分为共享锁和独占锁
66、JDBC中如何进行事务处理?
Connection提供了事务处理的方法,通过调用setAutoCommit(false)可以设置手动提交事务;当事务完成后用commit()显式提交事务;如果在事务处理过程中发生异常则通过rollback()进行事务回滚。除此之外,从JDBC 3.0中还引入了Savepoint(保存点)的概念,允许通过代码设置保存点并让事务回滚到指定的保存点。
67、简述正则表达式及其用途。
在编写处理字符串的程序时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。
68、Java中是如何支持正则表达式操作的?
Java中的String类提供了支持正则表达式操作的方法,包括:matches()、replaceAll()、replaceFirst()、split()。此外,Java中可以用Pattern类表示正则表达式对象,它提供了丰富的API进行各种正则表达式操作
69、写一个冒泡排序
import java.util.Comparator; /** * 排序器接口(策略模式: 将算法封装到具有共同接口的独立的类中使得它们可以相互替换) * @author骆昊 * */ public interface Sorter { /** * 排序 * @param list 待排序的数组 */ public <T extends Comparable<T>> void sort(T[] list); /** * 排序 * @param list 待排序的数组 * @param comp 比较两个对象的比较器 */ public <T> void sort(T[] list, Comparator<T> comp); }
import java.util.Comparator; /** * 冒泡排序 * * @author骆昊 * */ public class BubbleSorter implements Sorter { @Override public <T extends Comparable<T>> void sort(T[] list) { boolean swapped = true; for (int i = 1, len = list.length; i < len && swapped; ++i) { swapped = false; for (int j = 0; j < len - i; ++j) { if (list[j].compareTo(list[j + 1]) > 0) { T temp = list[j]; list[j] = list[j + 1]; list[j + 1] = temp; swapped = true; } } } } @Override public <T> void sort(T[] list, Comparator<T> comp) { boolean swapped = true; for (int i = 1, len = list.length; i < len && swapped; ++i) { swapped = false; for (int j = 0; j < len - i; ++j) { if (comp.compare(list[j], list[j + 1]) > 0) { T temp = list[j]; list[j] = list[j + 1]; list[j + 1] = temp; swapped = true; } } } } }
70、折半查找
折半查找,也称二分查找、二分搜索,是一种在有序数组中查找某一特定元素的搜索算法。搜素过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜素过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组已经为空,则表示找不到指定的元素。这种搜索算法每一次比较都使搜索范围缩小一半,其时间复杂度是O(logN)。
import java.util.Comparator; public class MyUtil { public static <T extends Comparable<T>> int binarySearch(T[] x, T key) { return binarySearch(x, 0, x.length- 1, key); } // 使用循环实现的二分查找 public static <T> int binarySearch(T[] x, T key, Comparator<T> comp) { int low = 0; int high = x.length - 1; while (low <= high) { int mid = (low + high) >>> 1; int cmp = comp.compare(x[mid], key); if (cmp < 0) { low= mid + 1; } else if (cmp > 0) { high= mid - 1; } else { return mid; } } return -1; } // 使用递归实现的二分查找 private static<T extends Comparable<T>> int binarySearch(T[] x, int low, int high, T key) { if(low <= high) { int mid = low + ((high -low) >> 1); if(key.compareTo(x[mid])== 0) { return mid; } else if(key.compareTo(x[mid])< 0) { return binarySearch(x,low, mid - 1, key); } else { return binarySearch(x,mid + 1, high, key); } } return -1; } }