4.1 Java基本概念
4.1.1 Java语言的有点
- Java是纯面向对象的语言;
- 平台无关性;一次编译,到处运行;
- Java提供多种内置库;
- 提供web应用开发的支持;
- 具有较好的安全性和健壮性;
java语言是c++改进并重新设计而来。
4.1.2 Java与C/C++有什么异同
- java是解释型语言:程序由Java编译器编译成字节码,然后由JVM执行。C/C++是编译型语言,源代码编译和链节后生成二进制代码。Java比C/C++慢,但是跨平台。
- Java纯面向对象,C++面向过程和对象,可以定义全局变量和全局函数。
- Java没有指针;
- Java不支持多继承;
- 自动gc
4.1.3 为什么又public static void main(String[] args)方法
- Java程序的入口方法;
- static表明该方法在静态存储区,类加载后就可以访问,类名.方法名;
- args是命令行下交互的方式;
引申:
- main()方法的其他定义格式:
– static public void main(String[] args)
– public static final void main(String[] args)
– static public synchronized void main(String[] args) - 一个.java文件中可以多个main()方法吗?
– 只有和文件名相同的用public修饰的类的main才可以作为程序入口。
4.1.4 静态块在类被加载时就调用
public class Test {
static {
System.out.println("hello world1");
}
public static void main(String[] args) {
System.out.println("hello world2");
}
}
结果
hello world1
hello world2
4.1.5 Java程序初始化的顺序
对象实例化之前,所在类的所有成员变量都会进行初始化。
三个原则:
- 静态对象(变量)优先初始化;静态只初始化一次。
- 父类优先子类初始化;
- 按成员变量定义顺序初始化。
例:
父类静态变量;
父类静态代码块;
子类静态变量;
子类静态代码块;
父类非静态变量;
父类非静态代码块;
父类构造函数;
子类非静态变量;
子类非静态代码块;
子类构造函数。
4.1.6 Java的作用域
4.1.7 一个Java文件是否可以定义多个类
可以。但是只有一个类用public修饰,并且名字与文件名相同。
javac编译的时候会生成多个.class文件。
4.1.8 什么是构造函数
- 构造函数名与类名相同,且没有返回值;
- 每个类可以有多个构造函数;
- 构造函数可以有0个或多个参数;
- 构造函数伴随new操作调用,必须由系统调用。
- 构造函数完成对象初始化;
- 构造函数不能被继承和覆盖,但可以重载;
- 子类可通过uper显示调用父类构造函数;
- 父类和子类都没有构造函数的时候,编译器会给父类和子类生成一个无参构造函数;
- 构造函数的修饰符与当前类的修饰符相同。
4.1.9 为什么接口没有实现方法
- 接口只包含方法的定义,不包含方法的实现;
- java8开始,接口默认方法是静态方法;
- 接口成员的修饰符都是public
- 接口常量值默认public static final
4.1.10 Java中的clone方法作用
- Java才采用基本类型(int, double, char)的时候都是值传递;
- 其他类型都按引用传递;
class Obj {
private int aInt = 0;
public int getaInt(){
return aInt;
}
public void setAInt(int init1) {
this.aInt = init1;
}
public void changeInt() {
this.aInt = 1;
}
}
public class TestRef {
public static void main(String[] args) {
Obj a = new Obj();
Obj b = a;
b.changeInt();
System.out.println("a: "+ a.getaInt());
System.out.println("b: "+b.getaInt());
}
}
结果:
a: 1
b: 1
- 使用clone()方法的步骤:
– 实现Cloneable()接口,Cloneable()接口是标识接口,没有任何接口方法;
– 在Object类中重写clone()方法;
– 在clone()方法中调用super.clone()方法。调用的是Obejct类的clone()方法;
– 把浅复制的引用指向原型对象新的克隆体。
class Obj implements Cloneable {
private int aInt = 0;
public int getaInt(){
return aInt;
}
public void setAInt(int init1) {
this.aInt = init1;
}
public void changeInt() {
this.aInt = 1;
}
public Object clone(){
Object o = null;
try {
o = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return o;
}
}
public class TestRef {
public static void main(String[] args) {
Obj a = new Obj();
Obj b = (Obj)a.clone();
b.changeInt();
System.out.println("a: "+ a.getaInt());
System.out.println("b: "+b.getaInt());
}
}
结果
a: 0
b: 1
当类中只有一些基本数据时,采用以上方法即可;
当类中包含对象时,就只能深复制
class Obj implements Cloneable {
private Date birth = new Date();
public Date getBirth(){
return birth;
}
public void setAInt(Date birth) {
this.birth = birth;
}
public void changeDate() {
this.birth.setMonth(4);
}
public Object clone(){
Obj o = null;
try {
o = (Obj)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
//实现深复制
o.birth = (Date)this.getBirth().clone();
return o;
}
}
public class TestRef {
public static void main(String[] args) {
Obj a = new Obj();
Obj b = (Obj)a.clone();
b.changeDate();
System.out.println("a = " + a.getBirth());
System.out.println("b = " + b.getBirth());
}
}
结果
a = Wed Sep 25 18:01:03 CST 2019
b = Sat May 25 18:01:03 CST 2019
- 浅复制与深复制的区别
– 浅复制只考虑复制的对象,而不考虑被复制对象引用的其他对象;
– 深复制不仅复制对象,还复制对象引用的对象。
4.1.11 什么是反射机制?
- 反射功能
– 得到一个对象所属的类;
– 得到一个类的所有成员变量和方法;
– 在运行时创建对象(重要,代码如下);
– 在运行时调用对象的方法;
class Base {
public void f() {
System.out.println("Base");
}
}
class Sub extends Base {
public void f() {
System.out.println("Sub");
}
}
public class Test {
public static void main(String[] args) {
try {
Class c = Class.forName("Sub");
Base b = (Base)c.newInstance();
b.f();
} catch (Exception e) {
e.printStackTrace();
}
}
}
结果:
Sub
-
获取类的方法
– Class.forName(“类的路径”);
– 类名.class;
– 实例.getClass() -
Java创建对象的方式:
– 通过new实例化一个对象;
– 通过反射机制创建对象;
– 通过clone()方法创建对象;
4.6 异常处理
4.6.1 finally块什么时候执行
- finally块的作用就是保证无论什么情况下一定会执行;
如下所示,finally一定是在方法里执行了。
public class Test3 {
public static int testFinally() {
try {
return 1;
} catch (Exception e) {
return 0;
} finally {
System.out.println("execute finally");
}
}
public static void main(String[] args) {
int result = testFinally();
System.out.println(result);
}
}
结果:
execute finally
1
- 如果finally有返回值,会覆盖前面的返回值;
public class Test3 {
public static int testFinally() {
try {
return 1;
} catch (Exception e) {
return 0;
} finally {
System.out.println("execute finally");
return 3;
}
}
public static void main(String[] args) {
int result = testFinally();
System.out.println(result);
}
}
结果
execute finally
3
- 对基本数据类型,finally中改变其值不会有影响,对引用类型会有影响。
public class Test3 {
public static int testFinally1() {
int result = 1;
try {
result = 2;
return result;
} catch (Exception e) {
return 0;
} finally {
result = 3;
System.out.println("execute finally1");
}
}
public static StringBuffer testFinally2(){
StringBuffer sb = new StringBuffer("Hello");
try {
return sb;
} catch (Exception e) {
return null;
} finally {
sb.append(" World");
System.out.println("execute finally2");
}
}
public static void main(String[] args) {
int resultVal = testFinally1();
System.out.println(resultVal);
StringBuffer resultRef = testFinally2();
System.out.println(resultRef);
}
}
结果:
execute finally1
2
execute finally2
Hello World
引申:finally中的代码一定会执行吗?
否,下面是两个例子;
- 程序进入try之前就出现异常,不会执行
public class Test3 {
public static int testFinally() {
int result = 1/0;
try {
result = 2;
return result;
} catch (Exception e) {
return 0;
} finally {
result = 3;
System.out.println("execute finally1");
}
}
}
- 在try中强制退出也不会
public class Test3 {
public static void testFinally() {
try {
System.out.println("try block");
System.exit(0);
} catch (Exception e) {
System.out.println(" catch block");;
} finally {
System.out.println("finally block");
}
}
public static void main(String[] args) {
testFinally();
}
}
4.9 Java Collections
4.9.1 Java Collections框架是什么
List、Set、Stack、Queue接口均继承自Collection接口。
- Set接口
元素不能重复,因此每个元素定义了equals()方法保证唯一性。TreeSet有序,HashSet无序。 - List接口
有序的,具体实现有ArrayList, LinkedList和Vector. - Map接口
值可以重复,但是键值唯一。
HashMap基于散列表实现,采用对象的hashCode快速查询。LinkedHashMap采用列表维护内部的顺序。TreeMap基于红黑树实现,内部元素按需排列。
4.9.2 什么是迭代器
- 迭代器使用示例:
public class IteratorTest {
public static void main(String[] args) {
List<String> ll = new ArrayList<String>();
ll.add("first");
ll.add("second");
ll.add("thrid");
ll.add("fourth");
for (Iterator<String> iter=ll.iterator(); iter.hasNext();) {
String str = iter.next();
System.out.println(str);
}
}
}
- 迭代器failed-fast原因:当调用容器的iterator()方法返回Iterator对象时,把容器中包含对象的个数赋值给一个expectedModCount, 在调用next()方法的时候会比较容器实际对象数modCount和expectedModCount是否相等,不相等则抛出ConcurrentModificationExpection.
引申
- 多线程情况下:JDK1.5以后ConcurrentHashMap和CopyOnWriteArrayList是线程安全的容器;
- Iterator和ListIterator的区别?
Iterator只能正向遍历集合。ListIterator继承自Iterator,可以从两个方向遍历List,同时支持元素修改。
ArrayList、Vector和LinkedList的区别
- ArrayList 底层数组实现 默认扩容1.5倍 随机访问O(1) 插入O(n);
- Vector 底层数组实现 synchronization同步 默认扩容2倍 性能低于ArrayList。
- LinkedList 底层双向列表 访问O(n) 插入O(1) 线程不安全。
HashMap、HashTable、TreeMap和WeakHashMap有哪些区别?
- HashMap 和HashTable的区别
– HashMap是HashTable的轻量级实现,HashMap允许插入一个key为null的值,在0位置,HashTable不可以;
– HashMap中是containsValue和containsKey方法,HashTable是contains方法;
– HashTable是线程安全的,使用了同步控制;
– HashTable使用Enumeration,HashMap使用Iterator
– HashMap和HashTable的hash/rehash方法几乎一致。
– HashTable大小默认11,扩容是old2+1, HashMap默认16,扩容old2; - TreeMap实现了sortedMap,能够保存的记录根据键排序。
- WeakHashMap的键采用弱引用,键不需要时GC就回收。
常见面试题
- HashTable中的同步?
同时只能一个线程可以修改hash表。 - 如何实现HashMap的同步?
Map m = Collections.synchronizedMap(new HashMap())
4.9.6 Collection和Collections的区别
- Collection是一个集合接口;
- Collections是一个服务Collection的包装类。