Java程序员面试宝典(二)

31. float和double型的取值范围

实型又称浮点型,用来表示带小数部分的实型数据。
浮点数的存储格式:符号位、指数位、尾数位。
实型数据存储格式
float 4个字节,范围从3.4E(-38)3.4E38。一个浮点数由2部分组成:底数m 和 指数e。±mantissa × 2^exponent。注意,公式中的mantissa 和 exponent使用二进制表示。指数部分占8 bit,由于有符号,所以指数部分的范围为-128128,而-2^(-128)2^128约等于3.4E(-38)3.4E38。精度(有效数字)看尾数部分。float类型一般在数字后面加f或F。
double 8个字节,范围从1.7E(-138)~1.7E138。

32. 利用BigDecimal类进行精确计算

通常float或double只能用于科学计算,在大多数商业计算中,需要用java.math.BigDecimal类。
使用BigDecimal类来进行计算的步骤:
(1)用BigDecimal的构造器把基本类型或其String类型转换成BigDecimal对象;用静态valueOf()方法把基本类型变量构建成BigDecimal对象;
(2)调用BigDecimal的加减乘除进行运算;
(3)将BigDecimal对象通过 Value()方法转换成float、double、int等类型。

1
2
3
4
5
double val = 1.0;
BigDecimal b1 = new BigDecimal(Double.toString(val)); //用基本类型变量构成BigDecimal对象
BigDecimal b2 = new BigDecimal(Double.toString(val)); //用基本类型的字符串构成BigDecimal对象
BigDecimal b3 = BigDecimal.valueOf(val); //用基本类型构成BigDecimal对象
b1.add(b2).doubleValue(); //执行BigDecimal的加法,并将其转换为double型。

33. boolean类型和Boolean类型的区别

对于C/C++,用0代表false,非0代表true。而Java的boolean类型只能用true或false来代表真假(所以if(100)这种是不能执行的,if(100>90)可以执行)。
其包装类Boolean类型通常有三种状态:true、false、null,初始化时通常初始化为false。
boolean型由于是基本数据类型,通常存放在栈空间;而Boolean类型对象存放在堆空间。

34. char的取值范围

char采用Unicode编码,每个字符占2个字节,则取值范围02^16-1。Unicode兼容ASCII码,英文字母0127。

1
2
char z = ‘中’; 
int i = z; //’中’字的Unicode编码值

35. 字符串字面量是否生成一个新的String对象

String str1 = “abc”; //JVM到String对象池中去搜索该字符串是否已经被创建,若创建则返回该对象的引用,否则先创建该字符串,再返回引用。
String str2 = new String(“abc”); //与str1的创建过程相同,还创建了一个新的String对象(即new关键字的作 用),并返回一个引用给str2。

36. 字符串对象池

JVM在启动时会实例化9个对象池,包括8个基本类型对应的包装类和String对象,主要是为了避免频繁地创建和销毁对象影响系统性能。

37. StringBuffer和StringBuilder

String具有不变性,它只能被创建,不能被改变。
StringBuilder不是线程安全的,它适用于单线程环境,例如SQL 语句的拼装、JSON封装等;
StringBuffer是线程安全的,它适用多线程环境,如XML解、HTTP参数解析和封装等。

38. 如何输出反转后的字符串

StringBuffer的reverse()方法

1
2
3
String s = “ab”;
StringBuffer sb = new StringBuffer(s);
sb.reverse().toString();

39. 如何使用指定字符集创建String对象

使用String类的带参构造器即可,参数包括两个:一个是指定字符集的byte数组,一个是字符集编码的字符串形式。

1
2
String a= “中文”;
String str = new String(a.getBytes(), “GBK”);

40. 理解数组在Java中是一个类

Java中只有8种基本数据类型,其它都是引用数据类型。
获取数组的类名:arr.getClass().getName();
数组的类名总是以“[”开头,后面跟的标示不同:int型是一个I字符;字符串类型是Ljava.lang.String。

41. 引用类型数组

引用类型数组中存放的是数据的引用。

1
2
3
Object[] obj = new Object[3];
obj[0]=new Object();
obj[2]=new Object(“abc”);

引用数据类型数组的内存结构

42. 如何拷贝数组的数据

由于引用类型数组中存的是数据的引用,因此不能直接对数组的内容进行复制。使用System.arrayCopy((Object src, int srcPos, Object dest, int destPos, int length))
其中
src - the source array.
srcPos - starting position in the source array.
dest - the destination array.
destPos - starting position in the destination data.
length - the number of array elements to be copied.

43. 二维数组

Java的二维数组是这样的:先创建一个一维数组,然后该数组的元素再引用另外一个一维数组。

44. 集合类

集合类只能存放对象,因此基础数据类型需要自动装箱成包装类才能存放。
1) Set:无序、不可重复的集合。HashSet, TreeSet
2) List:有序、可重复的集合。ArrayList, LinkedList, Vector
3) Queue:队列集合。ArrayDeque, LinkedList, Deque(ArrayDeque)
4) Map:具有映射关系的集合。HashMap, TreeMap, HashTable

Collection集合的继承树
Map集合的继承树

45. 迭代器(Iterator,Cursor游标)

Java提供Iterable和Iterator接口来遍历集合元素。迭代器提供一种访问集合对象中各个元素的途径,同时又不需要暴露该对象的内部细节。
与使用Iterator接口遍历集合元素类似的是,使用foreach循环迭代变量也不是集合对象本身,而是集合对象的内部的元素值。因此为了避免共享资源引发的潜在问题,不能修改集合对象的元素值,否则会引发ConcurrentModificationException异常。
迭代器的主要用法是,用hasNext()作为循环条件,再用next()方法得到下一个元素,最后再进行相关操作。

46. 比较器(Comparator)

比较器指的是集合存储的元素的特性。比较器用于比较自定义类的实例之间的大小。
Java提供Comparable和Comparator接口来比较集合元素。
比较器是把集合元素和数组强行按照指定的方式进行排序的对象,它是实现了Comparator接口的实例。如果一个集合元素是可比较的(实现了Comparable接口),则它就拥有了默认的排序方法,比较器则是强行改变它的默认比较方式来排序;如果集合元素不可比较(没有实现Comparable接口),则可通过实现Comparator接口来实现动态排序。
1) Comparable接口
进行比较的类先实现Comparable接口,然后重写其int compareTo(..)方法,一个参数,返回值大于0则表示本对象大于参数对象。Comparable接口也可单独成类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ComparableUser implements Comparable{
private String name;
private String age;
//getter、setter方法
//重写compareTo()方法
public int compareTo(Object o){
return this.age - ((Comparable) o ).getAge(); //或全用age
}
public static void main(String[] args){
ComparableUser user1 = new ComparableUser(“张三”, 20);
ComparableUser user2 = new ComparableUser(“李四”, 30);
if(user1.compareTo(user2) > 0) //用户1大于用户2
else if (…
}
}

2)Comparator接口
Comparator接口一般不被集合元素所实现,而是单独实现(单独一个类)或用匿名内部类的方式。它包含一个int compare()方法,两个参数,返回值与Comparable接口一样。

1
2
3
4
5
6
7
8
9
10
11
12
public class User{
private String name;
private String age;
//getter、setter方法
public static void main(String[] args){
User user1 = new User(“张三”, 20);
User user2 = new User(“李四”, 30);
Comparator com = new ComparatorUser();
if (com.compare(user1, user2) > 0) //用户1大于用户2
else if(…
}
}
1
2
3
4
5
6
7
8
/**单独一个实现类实现Comparator接口**/
public ComparatorUser implements Comparator{
public int compare(Object user1, Object user2){
User u1 = (User) user1;
User u2 = (User) user2;
return u1.getAge() – u2.getAge();
}
}

47. ArrayList(线程不安全)Vector(线程安全)的区别

ArrayList和Vector都是List接口的实现类,它们都代表链表形式的数据结构,且它们的实现方式非常类似,都是用一个对象数组来存储元素。
1) Vector大多数成员方法是使用synchronized关键字修饰的,也就是线程安全的;
2) 因此若不要求同步,ArrayList不是线程安全的,它的效率更高。
add()添加、remove()删除、size()得到集合元素数量。

48. HashMap(线程不安全)HashTable(线程安全)的区别

HashMap和HashTable都是Map接口的实现类,保存元素时都是无序的。
1) HashTable不允许key或value为null,它的方法是同步的,是线程安全的,使用Enumeration遍历,有一个contains()方法,功能和containsValue()方法一样,直接使用对象的hashCode();
2) 而HashMap允许key或value为null,它的方法不同步,是线程不安全的,使用Iterator遍历,需要重新计算hash值。

1
2
3
4
//通过key集合遍历集合value
for ( String key : map.keySet()){
System.out.println(key + “ : ” + map.get(key));
}

49. 集合使用泛型的好处

集合可以存放任何类型的元素,如果在存放元素之前,就能确定元素的类型,将会使代码更加简洁,避免手动进行类型转换。而常常我们并不确定最终我们要放进集合的数据类型,所以用一种通用的方式进行定义,而不必写出具体的类,这些未知的类会在真正使用的时候再确定。

50. 对集合对象中的元素进行排序(针对List集合)

集合框架中的List的实现有ArrayList、Vector、LinkedList,这些集合是没有排序功能的,我们使用java.util.Collections类中的sort()方法进行排序。若这个类实现了Comparable接口,则直接调用Colletions.sort()方法;如果这个类没有实现Comparator,就可以传递一个Comparator实例作为sort()的第二个参数进行排序。

51. 符合什么条件可以使用foreach循环

foreach起到替代迭代器的作用,从语法上来讲,数组或实现了Iterable接口的类实例,都可以使用foreach循环
可以自定义集合类,实现Iterable和Iterator接口。如

1
2
3
4
5
6
7
8
9
10
public class MyForeach{
public static void main(String[] args){
MyList list = new MyList();
list.getList().add(“a”);
list.getList().add(“b”);
for(String str : list){
System.out.println(s);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MyList implements Iterable<String>,Iterator<String>{
private int loc = 0; //定义当前数组下标
private List<String> list = new ArrayList<>(); //存储数据的序列
//是否有下一个元素
public boolean hasNext(){
return list.size > loc;
}
//得到下一个元素
public String next(){
return list.get(loc++);
}
//删除当前下标的元素
public void remove(){
list.remove(loc);
}
//得到迭代器
public Iterator<String> iterator(){
return this;
}
}

52. JFrame的作用

Java关于图形界面的开发有两套API,一个是AWT,一个是Swing。但AWT已慢慢退出历史舞台,大多数时候的Java图形界面开发指的就是Swing开发。
JFrame起到了Swing窗口的作用,其它一切的组件都在它的包含之内。
窗口中添加的内包括:按钮(Button)、文本框(Text)、滚动条(Scroll Bar)、标签(Label)等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import javax.swing.JFrame;
public class HelloJFram{
public static void mian(String[] args){
JFrame jf = new JFrame(“Hello JFrame”); //通过JFrame的构造方法显示窗口标题
//设置窗口在关闭时退出,结束程序
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setSize(200, 200);
jf.setVisible(true);
//为窗口添加一个标签
JLabel label = new JLabel();
label.setText(“This is one label”); //设置标签文本
jf.add(label); //将标签加到JFrame中
}
}

53. 创建按钮(JButton)

1
2
3
4
// 添加按钮前必须先设置布局格式,否则JFrame中无法加入JButton
jf.setLayout(new FlowLayout());
JButton jb = new JButton(“my button”);
jf.add(jb);

54. 使用文本输入组件(JTextField和JTextArea)

1)JTextField(文本框):只能单行输入;

1
2
3
4
jf.setLayout(new FlowLayout());
JTextField jtf = new JTextField(10); //文本框的绘制长度
jtf.setText(“初始化内容”); //传入文本框中的内容
jf.add(jtf);

2)JTextArea(文本域):允许多行输入。

1
JTextArea jta = new JTextArea(5, 10); //与JTextField不同之处就是文本域需要提供长和宽

55. 如何捕获事件

事件:一个对象A的状态改变了的时候,通知其他对象发生了这样一件事,让他做出反应。
两种事件模式,即推/拉模式。推模式:对象A状态改变了,通知其他对象做出反应,Java的大多数事件模式都属于这一种;拉模式:其他对象监听感兴趣的对象A(如WIN32的图形用户界面编程)。
一次事件模型中,往往有3中角色:
(1) 事件发生的主体:状态随用户操作而改变,如按钮;
(2) 监听器:实现了某种或某些监听接口的类实例,如监听按钮的监听器;
(3) 事件:代表一种动作,通过它获取用户操作的信息,如按钮被单击。
一般通过以下步骤来获取事件模型:
(1) 创建组件对象,如JButton;
(2) 创建实现了监听器接口或继承自某个适配器类的实现类,该监听器必须实现一个***Listener接口
(3) 调用组件对象的add***Listener()方法,为该组件添加监听器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class HelloEvent{
private static JTextField text = new JTextField(10);
public static void main(String[] args){
JFrame jf = new JFrame(“My Frame”);
jf.setLayout(new FlowLayout());
jf.add(text);
JButton bt = new JButton(“my button”);
jf.add(bt);
bt.addActionListener(new ActionListener(){ //为按钮添加事件
//定义事件回调方法
public void actionPerformed(ActionEvent e){
HelloEvent.text.setText(“按钮被点击了”); //动作
}
});
showMe(jf);
}
private static void(JFrame jf){
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setSize(200, 200);
jf.setVisible(true);
}
}
`

56. 使用BorderLayout布局

AWT提供了5种常用的布局管理器: BorderLayout、FlowLayout、GridLayout、GridBagLayout 、GardLayout;Swing还提供了1种:BoxLayout。
BorderLayout是Swing容器的默认布局管理器。BorderLayout把容器分为东南西北中5个部分,在JFrame中放置组件的时候,参数中多添加一项,指明组件的方位.

1
jf.add(new JButton(“north”), BorderLayout.NORTH));

57. 使用FlowLayout布局

FlowLayout是将组件从左往右、从上到下排列。

1
2
jf.setLayout(new FlowLayout());
for(int i = 0; i < 10; i++) jf.add(new JButton(i + “”)); //添加10个按钮

58. 使用GrideLayout布局

GridLayout是一种表格式的布局管理器,从左到右从上到下摆放组件,而且它会填满整个容器。创建GridLayout时指定行数和列数。

1
jf.setLayout(new GridLayout(10, 10));

59. 事件模型的通用规则

1) 所有的Swing组件都有add***Listener()和remove***Listener()方法,用于注册和取消注册事件监听器,***代表事件的类型和含义;
2) ***事件的监听器类型叫***Listener,它们是add***Listener()和remove***Listener()方法的参数类型;
3) ***事件的类名叫***Event,它常作为***Listener接口的方法参数。

1
2
3
4
5
bt.addActionListener(new ActionListener(){
pulic static void performedAction(ActionEvent e){

}
});

常用事件的监听器、事件类型、添加和取消事件注册方法

60. 监听器的适配器(adapter)的作用

为一个组件添加监听器就是加入一个监听器接口的类实例,调用add***Listener()方法即可。但是如果这个方法的接口太多,而我们只需要使用其中一些方法,这就比较浪费。适配器为一些监听器接口的方法提供默认的空实现,减少代码冗余。

1
2
3
4
5
jtf.addKeyListener(new KeyAdapter(){
pulic static void performedAction(KeyEvent e){

}
});

适配器是一个类,如果事件处理类已经继承自其它接口,就不能继承自适配器类了。也可以自定义适配器。