public String toString() { String faceStr = \; switch(face) {
case 1: faceStr = \; break; case 11: faceStr = \; break; case 12: faceStr = \; break; case 13: faceStr = \; break;
default: faceStr = String.valueOf(face); }
return suite + faceStr; } } }
测试代码: class PokerTest {
public static void main(String[] args) { Poker poker = new Poker();
poker.shuffle(); // 洗牌 Poker.Card c1 = poker.deal(0); // 发第一张牌 // 对于非静态内部类Card
// 只有通过其外部类Poker对象才能创建Card对象
Poker.Card c2 = poker.new Card(\红心\, 1); // 自己创建一张牌
System.out.println(c1); // 洗牌后的第一张 System.out.println(c2); // 打印: 红心A
16 / 253
} }
面试题 - 下面的代码哪些地方会产生编译错误? class Outer {
class Inner {}
public static void foo() { new Inner(); }
public void bar() { new Inner(); }
public static void main(String[] args) { new Inner(); } }
注意:Java中非静态内部类对象的创建要依赖其外部类对象,上面的面试题中foo和main方法都是静态方法,静态方法中没有this,也就是说没有所谓的外部类对象,因此无法创建内部类对象,如果要在静态方法中创建内部类对象,可以这样做: new Outer().new Inner();
?
1
25、Java 中会存在内存泄漏吗,请简单描述。
答:理论上Java因为有垃圾回收机制(GC)不会存在内存泄露问题(这也是Java被广泛使用于服务器端编程的一个重要原因);然而在实际开发中,可能会存在无用但可达的对象,这些对象不能被GC回收,因此也会导致内存泄露的发生。例如Hibernate的Session(一级缓存)中的对象属于持久态,垃圾回收器是不会回收这些对象的,然而这些对象中可能存在无用的垃圾对象,如果不及时关闭(close)或清空(flush)一级缓存就可能导致内存泄露。下面例子中的代码也会导致内存泄露。 import java.util.Arrays;
17 / 253
import java.util.EmptyStackException; public class MyStack
private static final int INIT_CAPACITY = 16;
public MyStack() {
elements = (T[]) new Object[INIT_CAPACITY]; }
public void push(T elem) { ensureCapacity(); elements[size++] = elem; }
public T pop() { if(size == 0)
throw new EmptyStackException(); return elements[--size]; }
private void ensureCapacity() { if(elements.length == size) {
elements = Arrays.copyOf(elements, 2 * size + 1); }
18 / 253
} }
上面的代码实现了一个栈(先进后出(FILO))结构,乍看之下似乎没有什么明显的问题,它甚至可以通过你编写的各种单元测试。然而其中的pop方法却存在内存泄露的问题,当我们用pop方法弹出栈中的对象时,该对象不会被当作垃圾回收,即使使用栈的程序不再引用这些对象,因为栈内部维护着对这些对象的过期引用(obsolete reference)。在支持垃圾回收的语言中,内存泄露是很隐蔽的,这种内存泄露其实就是无意识的对象保持。如果一个对象引用被无意识的保留起来了,那么垃圾回收器不会处理这个对象,也不会处理该对象引用的其他对象,即使这样的对象只有少数几个,也可能会导致很多的对象被排除在垃圾回收之外,从而对性能造成重大影响,极端情况下会引发Disk Paging(物理内存及硬盘的虚拟内存交换数据),甚至造成OutOfMemoryError。
26、抽象的(abstract)方法是否可同时是静态的(static),是否可同时是本地方法(native),是否可同时被synchronized修饰?
答:都不能。抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的。本地方法是由本地代码(如C代码)实现的方法,而抽象方法是没有实现的,也是矛盾的。synchronized和方法的实现细节有关,抽象方法不涉及实现细节,因此也是相互矛盾的。 27、阐述静态变量和实例变量的区别。
答:静态变量是被static修饰符修饰的变量,也称为类变量,它属于类,不属于类的任何一个对象,一个类不管创建多少个对象,静态变量在内存中有且仅有一个拷贝;实例变量必须依存于某一实例,需要先创建对象然后通过对象才能访问到它。静态变量可以实现让多个对象共享内存。
补充:在Java开发中,上下文类和工具类中通常会有大量的静态成员。 28、是否可以从一个静态(static)方法内部发出对非静态(non-static)方法的调用? 答:不可以,静态方法只能访问静态成员,因为非静态方法的调用要先创建对象,在调用静态方法时可能对象并没有被初始化。 29、如何实现对象克隆? 答:有两种方式:
1). 实现Cloneable接口并重写Object类中的clone()方法;
2). 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆,代码如下。
import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;
19 / 253
public class MyUtil {
private MyUtil() {
throw new AssertionError(); }
public static
ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bout); oos.writeObject(obj);
ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bin); return (T) ois.readObject();
// 说明:调用ByteArrayInputStream或ByteArrayOutputStream对象的close方法没有任何意义
// 这两个基于内存的流只要垃圾回收器清理对象就能够释放资源,这一点不同于对外部资源(如文件流)的释放 } }
下面是测试代码:
import java.io.Serializable; /** * 人类 * @author 骆昊
20 / 253