Java基础知识
说明:其中内容摘自:《Java程序员面试宝典》,这里仅供学习使用。
1. Java语言有什么优点:
- Java为纯面向对象的语言(Thinking in Java:Everything is Object!)
- 平台无关。Java为解释性语言,Java代码通过编译器变成中间代码,在JVM中进行解释运行。
- 提供了众多的内置类库,方便开发。
- 提供了对Web应用和大数据处理的支持。
- 较好的安全性和健壮性。例如Java的强类型机制,GC机制,异常处理等
2. 为什么需要public staic void main(String[] args) {} 这个方法?
public staic void main(String[] args) {} 这个为Java程序的入口方法,JVM在运行程序加载类时,会首先查找main方法。public关键词表明了任何类或对象都能够访问这个方法,static修饰符表示该方法位于静态存储区中,只要该类被加载后,就可以使用该方法而不需要通过实例化对象来访问。
如何实现在main方法之前输出“Hello World!”?
使用静态代码块的方式:
static {
System.out.println("Hello World!");
}
静态代码块在类加载的时候就会直接调用,所以是在main方法被调用前执行。静态代码块在类中的位置并不影响它的执行顺序,它都会在main方法执行之前执行。
3. Java程序初始化顺序是什么?
在实例化对象时,对象所在类的成员变量首先要进行初始化,所有成员变量初始化完成后,才会调用该对象的构造方法创建对象。
Java程序的初始化过程一般遵循3个原则(优先级递减):
- 静态对象优于非静态对象的初始化。并且,静态对象只初始化一次,而非静态变量可能初始化多次。
- 父类优先于子类进行初始化。
- 按照成员变量的定义顺序进行初始化。
所以java对象的初始化顺序如下:父类静态变量 -> 父类静态代码块 -> 子类静态变量 -> 子类静态代码块 -> 父类非静态变量 -> 父类非静态代码块 -> 父类构造函数 -> 子类非静态变量 -> 子类非静态代码块 -> 子类构造方法。
4. Java中有哪些作用域?
作用域 | 当前类 | 同一package | 子类 | 其他package |
public | yes | yes | yes | yes |
protected | yes | yes | yes |
no |
friendly(default) | yes | yes | no | no |
private | yes | no | no | no |
5. 一个Java文件中是否可以定义多个类?
可以,但最多只能有一个类被public修饰,并且该类的名称必须和文件名相同。
6. 什么是构造函数?
构造函数是java类中的一种特殊的函数,用来在初始化类的对象时,初始化类中的成员变量。它具有如下特点:
- 构造函数一定要和类名相同。并且不能有返回值,和返回值类型。
- 一个类可以有多个构造函数(参数的类型不同或参数的个数不同)
- 它的主要工作就是在实例化对象时实例化类中的成员变量。
- 构造函数不能被继承。
- 子类可以通过super关键字显示的调用父类的构造函数。但当父类没有提供无参构造函数时,子类必须显示的调用父类的构造函数。如果父类提供了无参构造函数,则可以不显示的调用。
- 当父类和子类都没有定义构造函数时,编译器会为父类和子类都自动生成一个默认的无参构造函数。
7. 为什么Java中有些接口没有任何方法?
这些接口为标识接口,它对实现它的类没有任何语义上的要求,仅仅充当一个标识作用,用来表明实现它的类属于一个特定的类型。Java类库中目前存在的标识接口有Cloneable和Serializable,在使用时通常会用instanceOf()来判断实例对象的类型是否实现了一个给定的标识接口。
此外,interface中为一个抽象方法的集合,其中所有的成员的作用域为public,常量的默认修饰为public static final。
8. Java中的clone方法有什么作用?
首先明确一个问题,对象和对象的引用(实际上是地址)。Java在处理基本数据类型时(byte,short,int,long,float,double,boolean,char),都是采用值传递的方式(传递的时输入参数的复制),而对于其他类型都是引用传递(传递的是对象的一个引用)。即深浅拷贝的问题:
- 浅拷贝:被复制对象的所有变量都含有与原来对象相同的值,而所有对其他对象的引用仍指向原来的对象。即浅拷贝只复制对象本身,而不复制它所引用的对象。
- 深拷贝:被复制对象的所有变量都含有与原来对象相同的值,引用其他对象的变量将指向被复制的新对象,而不是指向原有的对象。
chone方法可用于实现浅拷贝和深拷贝。对于它的用法如下:
- 实现cloneable标识接口(上面提到过),其中没有什么需要实现的方法。
- 在类中重写Object的clone方法
- 在clone方法中调用super.clone()
- 把浅复制的应用执行新创建的克隆对象
当被复制的类中只有基本数据类型时,通过以上方法实现的为深拷贝,而如果类中存在引用变量,则实现的为浅拷贝。通过下面方法实现存在引用变量类的深拷贝。
- 在对象调用clone方法完成复制后,需要对对象的应用变量也调用clone方法。
9. 什么是反射机制?
它具有如下作用:
- 得到一个对象所属的类
- 获取一个类的所有成员变量和方法
- 在运行时动态的创建类的对象(主要作用)
- 在运行时调用对象的方法
在反射机制中,通过如下方式可以获取class类:
- class.forName(“类的路径”);
- 类名.class
- 实例.getClass();
Java中可以通过如下四种方式实例化一个对象:
- new关键字
- 通过clone方法
- 反射机制创建一个对象
- 通过反序列化的方式
10. Package的作用是什么?
- 提供多层命名空间,避免类的命名冲突
- 对类进行按功能分类,使项目的组织更加清晰。
11. Java中如何实现类似函数指针的功能?
函数指针的主要功能是回调函数,他是指函数现在某处注册,而它在稍后某个需要的时候被调用。Java中没有指针的概念,可以使用接口和类的方式实现类似的功能。具体的,先定义一个接口,接口中声明要调用的方法,接着根据功能的不同实现这个接口,最后把实现类的对象作为参数传递给调用程序,调用程序通过这个参数来调用指定的函数,从而实现回调函数的功能。例如如下的代码:
interface IntCompare {
public int cmp(int a, int b);
}
class Cmp1 implments IntCompare {
public int cmp(int a, int b) {
if (a>b) return 1;
else if (a<b) return -1;
else return 0;
}
}
class Cmp2 implements IntCompare {
public int com(int a, int b) {
if (a>b) return -1;
else if (a<b) return 1;
else return 0;
}
}
public class Test {
public static void insertSor(int[] a, IntCompare cmp) {
if (a!=null) {
for (int i = 0; i<a.length; i++) {
int temp = a[i]; j = i;
if (cmp.cmp(a[j-1], temp) == 1) {
while(j>=1 && cmp.cmp(a[j-1], temp) == 1) {
a[j] = a[j-1];
j--;
}
}
a[j] = temp;
}
}
}
public static void main(String[] args) {
int[] array = {};
insertSort(array, new Cmp1()); // 升序插入排序
insertSort(array, new Cmp2()); // 降序插入排序
}
}
其实这里是策略模式中所用到的思想。