Java SE基础题目一
1、面向过程和面向对象区别:
面向过程更注重处理事情的每一个步骤及顺序;而面向对象更关注事情有哪些参与者(对象),以及各自完成功能。以洗衣机洗衣服为例:
面向过程会将任务拆分为一系列的步骤(函数) ,如打开洗衣机---->放衣服----->加入洗衣粉----->清洗。
面向对象会拆分为人和洗衣机两个对象。其中人具有打开洗衣机、放衣服、加入洗衣粉功能;而洗衣机对象具有清洗功能。
对比:面向过程直接高效,针对事情本身;而面向对象更易于复用,便于拓展,比较繁琐。
2、面向对象三大特性:
2.1、封装
内部细节对外部调用透明(外部调用不用关心内部代码实现)。Java中两个封装的典型案例:
(1)、Java Bean的属性私有,通过getter/setter获取或赋值。在这里属性的赋值或者获取逻辑只能通过对象本身定义,不能由外部随意修改。
(2)、ORM(对象关系映射框架):引入MyBatis后,我们只用调用对应接口,不用关心连接如何建立,sql如何执行等等。
2.2、继承
继承基类(父类)共性的属性和方法,同时还可以做出自己的改变和个性化操作。Java是单继承的,注意super和this关键字。
2.3、多态
同一个行为具有多个不同表现形式或形态的能力。多态存在的三个必要条件:继承、重写、父类引用子类对象。Java中常见的两种多态使用场景:
(1)、父类类型 变量名 = new 子类类型
(2)、接口 变量名 = new 接口实现类
缺点:如果是子类或者接口实现类所独有的方法或者属性,通过上面的变量名是无法调用的,需要强制类型转换。
3、JDK、JRE、JVM区别和联系:
Java Development Kit:Java开发工具
Java Runtime Environment:Java运行时环境,为要运行Java程序的人员准备的。非开发人员,仅仅想运行Java看看的人。
Java Virtual Machine:Java虚拟机。
JDK = JRE + Java工具(javac、jconsole等)
JRE = JVM + lib类库
4、==、equals()和hashCode():
==对比的是栈中的值。其中基本数据类型直接比较变量值;引用数据类型比较堆中内存对象地址(引用地址)。
equals()方法是Object类中方法,默认也是采用==比较,但是String重写了它,用于比较两个字符串内容是否相同。
如果我们想要比较两个对象是否相等,也可以在类中重写equals()方法,一般也会重写hashCode()方法【了解过HashSet底层的都知道】。
1、练习题:Employee对象有属性name和age,只有当name和age都相同时
才认为是同一个对象。现在HashSet添加多个Employee,要让相同对象
不能进入HashSet。
public class Employee {
private String name;
private Integer age;
@Override
public boolean equals(Object obj) {
// 因为HashSet底层会通过equals()判断两个对象是否相同,所以重写。
if (this == obj) { // 内存地址相同
return true;
}
if (obj instanceof Employee) {
Employee employee = (Employee) obj;
if (this.name.equals(employee.getName()) && this.age == employee.getAge()) {
return true;
}
return false;
}
return false;// 类型都不相同,没有比较必要了
}
@Override
public int hashCode() {
// 3个不同对象的hashCode()值一定不同,转换后的索引值也不同。因此3个对象会进入不同table下标处。
// 所以这就是为什么重写equals()方法一般都要重写hashCode()方法!!!
return Objects.hash(name, age);
}
public Employee() {
}
public Employee(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Employee [name=" + name + ", age=" + age + "]";
}
}
public class MyHashSet {
public static void main(String[] args) {
HashSet<Employee> set = new HashSet<>();
set.add(new Employee("ABC", 20));
set.add(new Employee("ABC", 18));
set.add(new Employee("ABC", 20));
System.out.println("size---->" + set.size());
}
}
2、练习题:判断下面输出true/false:
Integer a = 128;
Integer b = 128;
System.out.println(a == b); // false
Integer c = 10;
Integer d = 10;
System.out.println(c == d); // true
Integer e = new Integer(10);
Integer f = new Integer(10);
System.out.println(e == f);//false
StringBuffer buffer = new StringBuffer("ABC");
StringBuffer buffer2 = new StringBuffer("ABC");
System.out.println(buffer.equals(buffer2)); // false
原因分析:
1、/*
*三种方式生成一个Integer对象:
* a、Integer a = 10;
* b、Integer a = new Integer(10);
* c、Integer a = Integer.valueOf(10);
*
* 实际上a等价于c。因为a会调用Integer.valueOf()静态方法生成一个Integer对象【jdk1.5后的自动装箱】
* 走进Integer.valueOf()源码:
* public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
这里low为-128,high为127。即只要是通过Integer.valueOf()静态方法生成的在[-128,127]之间的,都会
存入cache数组中,而超过这个范围会new Integer(i)生成对象。
结论:c和d比较地址都是cache数组中的同一个元素地址;
a和b、e和f比较地址是两个完全不同对象的地址。
*/
2、StringBuffer类没有重写equals() 方法。
5、final:
5.1、final的作用:
修饰类:该类不可继承。
修饰变量:该变量一旦被赋值就不可被更改。
修饰方法:该方法不可被子类重写,允许重载。
1、final修饰普通成员变量,可以在非静态代码块、声明变量时、构造方法执行初始化。
例如:final int a = 0;//声明变量时赋值
{
a = 0;//或者在非静态代码块中赋值
}
2、final修饰类成员变量,只能在静态代码块、声明变量时执行初始化。
例如:final static int b = 0;//声明变量时赋值
static{
b = 0;//静态代码块赋值
}
3、final修饰局部变量,使用该变量前必须赋值。
5.2、为什么内部类只能访问局部final变量?
public class Test {
private int a = 0;
public static void main(String[] args) {
}
public void test(final int b) { //局部变量final
final int c = 100; //局部变量final
//匿名内部类
new Thread() {
public void run() {
System.out.println("成员变量:"+a);
System.out.println("匿名内部类访问:"+b+" "+c);
};
}.start();
}
}
内部类和外部类是同一级别的,内部类不会随着外部类方法执行完
而被销毁。那么当外部类方法执行完,局部变量会被GC回收,但是
内部类还需要访问局部变量,怎么办???
为了解决该问题,JVM将局部变量复制一份作为内部类的成员变量,这样
即使局部变量GC回收,内部类仍可以访问。与此同时,我们必须保证
局部变量copy一份后与内部类的成员变量一致,即内部类不能修改局部
变量,因此使用final。
6、接口和抽象类区别:
1、抽象类中除了抽象方法还可以有普通成员方法;而接口只能存在抽象方法。
2、抽象类只能继承一个;而接口可以实现多个。
3、抽象类中成员变量可以是各种类型的;而接口只能是常量类型(public static final,默认的)。
4、设计目的不同:接口的设计目的是提供一种机制(功能),可以强制要求不同的类具有相同的行为。抽象类的设计目的是代码复用,它抽取各个子类共同行为作为成员方法;不同行为作为抽象方法,让各个子类独自实现。
7、Java异常体系:
1、所有异常都来自顶级父类Throwable。
2、Throwable下有两个子类Exception和Error。其中Error是程序无法处理的错误,一旦出现程序被迫停止。而Exception不会导致程序停止,又分为两个部分:RuntimeException运行时异常和CheckedException检查时异常。
3、CheckedException常常发生在程序编译过程,导致程序编译不通过。而RuntimeException常常发生在程序运行过程中,导致当前线程执行失败。