1. 面向对象的特征有哪些方面?
1) 抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面
2) 封装:把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口
3)继承: 继承是从已有类得到继承信息创建新类的过程,提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)
4)多态:指允许不同子类型的对象对同一行为作出不同的响应
2. Java中作用域public, protected,default,private有什么区别?
3.short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗?
对于short s1 = 1; s1 = s1 + 1;由于1是int类型,因此s1+1运算结果也是int 型,需要强制转换类型才能赋值给short型。而short s1 = 1; s1 += 1;可以正确编译,因为s1+= 1;相当于s1 = (short)(s1 + 1);+=这种赋值运算符隐含了强制类型转换。
4.int和Integer有什么区别?
Integer是int的包装类,为了编程方便引入基本数据类型,但是,想像操作对象一样操作基本数据类型,所以引入了包装类。Java1.5以后引入了自动拆箱和自动装箱机制,使得二者可以相互转换。
- 原始类型:boolean,char,byte,short,int,long,float,double
- 包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
面试题一:
public static void main(String[] args) {
Integer a = new Integer(3);
Integer b = 3;
int c = 3;
System.out.println(a==b); // false 两个引用没有引用同一对象
System.out.println(a==c); // true a自动拆箱成int类型再和c比较
}
注:如果整型字面量的值在-128到127之间,那么不会new新的Integer对象,而是直接引用常量池中的Integer对象
面试题二:
public static void main(String[] args) {
Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150;
System.out.println(f1 == f2); // true
System.out.println(f3 == f4); // false
}
当我们给一个Integer对象赋一个int值的时候,会调用Integer类的静态方法valueOf,如果看看valueOf的源代码就知道发生了什么。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
如果整型字面量的值在-128到127之间,那么不会new新的Integer对象,而是直接引用常量池中的Integer对象,所以上面的面试题中f1==f2的结果是true,而f3==f4的结果是false。
5.解释内存中的栈(stack)、堆(heap)和方法区(method area)的用法。
栈:基本数据类型的变量+对象的引用变量+实例方法
堆:实例对象
方法区:类的结构信息、常量、静态变量等,程序中直接书写的字面量,如:100,‘hello’和常量都是放在常量池中,常量池是方法区的一部分。
堆和方法区线程共享,栈是线程私有,栈空间很小但是操作起来最快,栈和堆的大小可以通过JVM的启动参数进行调整,栈空间用光了会报StackOverflowError,堆和方法区空间不足会报OutOfMemoryError
6.构造器(constructor)是否可被重写(override)?
构造器不能被继承,因此不能被重写,但可以被重载。
7.两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对?
不对,如果两个对象x和y满足x.equals(y) == true,它们的哈希码(hash code)应当相同。
补充:
equals方法必须满足自反性(x.equals(x)必须返回true)、对称性(x.equals(y)返回true时,y.equals(x)也必须返回true)、传递性(x.equals(y)和y.equals(z)都返回true时,x.equals(z)也必须返回true)和一致性(当x和y引用的对象信息没有被修改时,多次调用x.equals(y)应该得到同样的返回值),而且对于任何非null值的引用x,x.equals(null)必须返回false。
8.当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?
值传递。Java中只有值传递,基本数据类型传递的是值的副本,引用数据类型传递的是引用的拷贝。
9.String和StringBuilder、StringBuffer的区别?
String是字符串常量,StringBuilder是字符串变量是线程安全的,StringBuffer是字符串变量是线程不安全的。
public static void main(String[] args) {
String s1 = "Programming";
String s2 = new String("Programming");
String s3 = "Program";
String s4 = "ming";
String s5 = "Program" + "ming";
String s6 = s3 + s4;
System.out.println(s1 == s2); // false
System.out.println(s1 == s5); // true
System.out.println(s1 == s6); // false
System.out.println(s1 == s6.intern()); // true
System.out.println(s2 == s2.intern()); // false
}
补充:
String对象的intern方法会得到字符串对象在常量池中对应的版本的引用(如果常量池中有一个字符串与String对象的equals结果是true),如果常量池中没有对应的字符串,则该字符串将被添加到常量池中,然后返回常量池中字符串的引用。
10.重载(Overload)和重写(Override)的区别。重载的方法能否根据返回类型进行区分?
方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性
,而后者实现的是运行时的多态性
。重载发生在一个类中
,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;重写发生在子类与父类之间
,重写要求子类被重写方法与父类被重写方法有相同的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则
)。重载对返回类型没有特殊的要求。
11.描述一下JVM加载class文件的原理机制
JVM中类的装载是由类加载器(ClassLoader)和它的子类
来实现的,Java中的类加载器是一个重要的Java运行时系统组件
,它负责在运行时查找和装入类文件中的类。
由于Java的跨平台性
,经过编译的Java源程序并不是一个可执行程序,而是一个或多个类文件。当Java程序需要使用某个类时,JVM会确保这个类已经被加载
、连接(验证、准备和解析)
和初始化
。类的加载是指把类的.class文件中的数据读入到内存中
,通常是创建一个字节数组读入.class文件,然后产生与所加载类对应的Class对象。加载完成后,Class对象还不完整,所以此时的类还不可用。当类被加载后就进入连接阶段,这一阶段包括验证、准备(为静态变量分配内存并设置默认的初始值)和解析(将符号引用替换为直接引用)三个步骤。最后JVM对类进行初始化,包括:1) 如果类存在直接的父类并且这个类还没有被初始化,那么就先初始化父类;2) 如果类中存在初始化语句,就依次执行这些初始化语句。
类的加载是由类加载器完成的,类加载器包括:根加载器(BootStrap)、扩展加载器(Extension)、系统加载器(System)和用户自定义类加载器(java.lang.ClassLoader的子类)。从Java 2(JDK 1.2)开始,类加载过程采取了父亲委托机制(PDM)
。PDM更好的保证了Java平台的安全性,在该机制中,JVM自带的Bootstrap是根加载器,其他的加载器都有且仅有一个父类加载器。类的加载首先请求父类加载器加载,父类加载器无能为力时才由其子类加载器自行加载。JVM不会向Java程序提供对Bootstrap的引用。
下面是关于几个类加载器的说明:
Bootstrap:一般用本地代码实现,负责加载JVM基础核心类库(rt.jar);
Extension:从java.ext.dirs系统属性所指定的目录中加载类库,它的父加载器是Bootstrap;
System:又叫应用类加载器,其父类是Extension。它是应用最广泛的类加载器。它从环境变量classpath或者系统属性java.class.path所指定的目录中记载类,是用户自定义加载器的默认父加载器。
13.char 型变量中能不能存贮一个中文汉字,为什么?
char类型可以存储一个中文汉字,因为Java中使用的编码是Unicode(不选择任何特定的编码,直接使用字符在字符集中的编号,这是统一的唯一方法),一个char类型占2个字节(16比特),所以放一个中文是没问题的。
补充:使用Unicode意味着字符在JVM内部和外部有不同的表现形式,在JVM内部都是Unicode,当这个字符被从JVM内部转移到外部时(例如存入文件系统中),需要进行编码转换。所以Java中有字节流和字符流,以及在字符流和字节流之间进行转换的转换流,如InputStreamReader和OutputStreamReader,这两个类是字节流和字符流之间的适配器类,承担了编码转换的任务
14.抽象类(abstract class)和接口(interface)有什么异同?
抽象类和接口都不能够实例化,但可以定义抽象类和接口类型的引用。一个类如果继承了某个抽象类或者实现了某个接口都需要对其中的抽象方法全部进行实现,否则该类仍然需要被声明为抽象类。抽象类中的成员可以是private、默认、protected、public的,而接口中的成员全都是public的。抽象类中可以定义成员变量,而接口中定义的成员变量实际上都是常量。有抽象方法的类必须被声明为抽象类,而抽象类未必要有抽象方法。
15.静态嵌套类(Static Nested Class)和内部类(Inner Class)的不同?
Static Nested Class是被声明为静态(static)的内部类,它可以不依赖于外部类实例被实例化。而通常的内部类需要在外部类实例化后才能实例化,其语法看起来挺诡异的。
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();
16.Java中会存在内存泄漏吗?请简单描述。
理论上Java因为有垃圾回收机制(GC)不会存在内存泄露问题(这也是Java被广泛使用于服务器端编程
的一个重要原因);然而在实际开发中,可能会存在无用但可达的对象
,这些对象不能被GC回收,因此也会导致内存泄露的发生。例如Hibernate的Session(一级缓存)中的对象属于持久态,垃圾回收器是不会回收这些对象的,然而这些对象中可能存在无用的垃圾对象,如果不及时关闭(close)或清空(flush)一级缓存就可能导致内存泄露。下面例子中的代码也会导致内存泄露。
import java.util.Arrays;
import java.util.EmptyStackException;
public class MyStack<T> {
private T[] elements;
private int size = 0;
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);
}
}
}
上面的代码实现了一个栈(先进后出(FILO))结构,乍看之下似乎没有什么明显的问题,它甚至可以通过你编写的各种单元测试。然而其中的pop方法却存在内存泄露的问题,当我们用pop方法弹出栈中的对象时,该对象不会被当作垃圾回收,即使使用栈的程序不再引用这些对象,因为栈内部维护着对这些对象的过期引用(obsolete reference)。在支持垃圾回收的语言中,内存泄露是很隐蔽的,这种内存泄露其实就是无意识的对象保持
。如果一个对象引用被无意识的保留起来了,那么垃圾回收器不会处理这个对象,也不会处理该对象引用的其他对象,即使这样的对象只有少数几个,也可能会导致很多的对象被排除在垃圾回收之外,从而对性能造成重大影响,极端情况下会引发Disk Paging(物理内存与硬盘的虚拟内存交换数据),甚至造成OutOfMemoryError。
17.抽象的(abstract)方法是否可同时是静态的(static),是否可同时是本地方法(native),是否可同时被synchronized修饰?
都不能。抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的。本地方法是由本地代码(如C代码)实现的方法,而抽象方法是没有实现的,也是矛盾的。synchronized和方法的实现细节有关,抽象方法不涉及实现细节,因此也是相互矛盾的
。
18.如何实现对象克隆
实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆
public Object clone(){
ObjectInputStream ois = null;
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream(); // 输出流
ObjectOutputStream oos = new ObjectOutputStream(bos); // 对象输出流
oos.writeObject(this); // 写出对象
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); // 输入流
ois = new ObjectInputStream(bis); // 对象输入流
return ois.readObject(); // 读取对象
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
19.GC是什么?为什么要有GC?
垃圾回收器(Garbage Collection)是JVM自带的一个线程(自动运行着的程序),用于回收没有任何引用所指向的对象。使得程序员可以不用担心内存管理,因为垃圾收集器会自动管理。
GC线程会从栈中的引用变量开始跟踪,从而判断哪些内存是正在使用的,若GC无法跟踪到某一块内存,那么GC就认为这块内存不再使用了,即为可回收的。
20. String s = new String("xyz");创建了几个字符串对象?
两个对象,一个是静态区的"xyz",一个是用new创建在堆上的对象
21. 接口是否可继承(extends)接口?抽象类是否可实现(implements)接口?抽象类是否可继承具体类(concrete class)?
接口可以继承接口,而且支持多重继承。抽象类可以实现(implements)接口,抽象类可继承具体类也可以继承抽象类。
22. 一个".java"源文件中是否可以包含多个类(不是内部类)?有什么限制?
可以,但一个源文件中最多只能有一个公开类(public class)而且文件名必须和公开类的类名完全保持一致。
23. Anonymous Inner Class(匿名内部类)是否可以继承其它类?是否可以实现接口?
可以继承其他类或实现其他接口,在Swing编程和Android开发中常用此方式来实现事件监听和回调。
24. 内部类可以引用它的包含类(外部类)的成员吗?有没有什么限制?
一个内部类对象可以访问创建它的外部类对象的成员,包括私有成员
25. Java 中的final关键字有哪些用法?
final修饰类,类不能被继承
final修饰方法,方法不能被重写,对重载不影响
final修饰变量,变量不能被修改
26. 指出程序的运行结果
public class Test {
public static void main(String[] args) {
A a = new B(); // 1a2b
a = new B(); // 2b
}
}
class A{
static{
System.out.println("1");
}
public A(){
System.out.println("2");
}
}
class B extends A{
static{
System.out.println("a");
}
public B(){
System.out.println("b");
}
}
创建对象时构造器的调用顺序是:先初始化静态成员,然后调用父类构造器,再初始化非静态成员,最后调用自身构造器。
27. 数据类型之间的转换
如何将字符串转换为基本数据类型?
调用基本数据类型对应的包装类中的方法parseXXX(String)或valueOf(String),即可返回相应基本数据类型
如何将基本数据类型转化为字符串?
1. 使用+与“”链接,即转为字符串。2.使用String.valueOf()即返回字符串
28. 如何实现字符串的反转和替换
public static String reverse(String originStr){
if(originStr == null || originStr.length() <= 1){
return originStr;
}
return reverse(originStr.substring(1))+originStr.charAt(0);
}
29. 怎样将GB2312编码的字符串转换为ISO-8859-1编码的字符串
String s1 = "您好";
String s2 = new String(s1.getBytes("GB2312"),"ISO-8859-1");
30. 日期和时间
1. 如何取得年月日、时分秒?
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));
System.out.println(cal.get(Calendar.MINUTE));
System.out.println(cal.get(Calendar.SECOND));
System.out.println("=========================");
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());
}