刷面试题第一、二天
一、面向对象
封装:封装的意义,在于明确标识出允许外部使用的所有成员函数和数据项。
内部细节对外部调用透明,外部调用无需修改或者关于内部实现。
继承:继承基类的方法,并做出自己的改变和扩展。
子类共性的方法或者属性直接使用父类的,而不需要自己再定义,只需扩展自己个性化的的,实现代码的复用。
多态:基于对象的所属类的不同,外部对同一个方法的调用,实际执行的逻辑不同。
多态存在的三个必要条件:
1、继承
2、重写
3、父类引用子类对象
Parent p = new Child();
p.方法名();
父类类型 变量名 = new 子类对象;
变量名.方法名();
若要使得同一个外部操作具有不同的行为,只需要将我们的子类对象进行修改。
无法调用子类特有的功能;
二、JDK JRE JVM
JDK: Java Development Kit ----- java开发工具包
JRE: Java Runtime Environment -- java运行时环境
JVM: Java Virtual Machine ------- java虚拟机
JDK 包含两个重要的组成部分:
1、JRE 和 java 工具
JRE 主要有两个文件夹,一个是bin(存放的是jvm),另一个是lib(java核心类库)
java工具主要包括javac、java、jconsole
2、java的跨平台是怎么实现的与特性?
java的优点在于一处编译,到处执行(针对的是.class文件)。我们编写的java程序 .java文件最终都会通过java工具中的javac指令编译成.class文件,.class文件最终会转换为我们操作系统可以识别的机器码,然而由于操作系统的底层实现不同,相同的.class文件在不同的操作系统转换得出的机器码都不同,针对这个问题,jvm对多种的操作系统提供了不同版本(jvm本身做了适配),再将我们的机器码映射到我们的操作系统进行相应的系统调用。
三、==和equals区别
1、==对比的是栈中的值。==对于基本数据类型比较的是变量值,对于引用数据类型比较的是堆中存放内存对象的地址。
2、equals()指的是值是否相同。
3、基本数据类型没有equals()方法,引用数据类型才有equals方法
四、final
最终的
修饰类:表示类不可修饰
修饰方法:表示方法不可被子类覆盖,但是可以重载
修饰变量:表示变量一旦被赋值不可以更改它的值
(1)修饰成员变量
如果final修饰的是类变量,只能在声明该类变量时指定初始值或在静态初始化代码块中指定初始值。
如果final修饰的是成员变量,可以在非静态初始化块、声明该变量或者构造器中执行初始化。
(2)修饰局部变量
系统不会为局部变量进行初始化,局部变量必须由程序员显式初始化。因此使用final修饰局部变量时,即可以在定义时便赋予初始值,也可以不指定默认值,在后面的代码中对final变量赋初值(仅一次)
public class FinalVar {
final static int a = 0;//再声明的时候就需要赋值 或者静态代码块赋值
/**
static{
a = 0;
}
*/
final int b = 0;//再声明的时候就需要赋值 或者代码块中赋值或者构造器赋值
/*{
b = 0;
}*/
public static void main(String[] args) {
final int localA; //局部变量只声明没有初始化,不会报错,与final无关。
localA = 0; //在使用之前一定要赋值
localA = 1; //但是不允许第二次赋值
}
}
(3)修饰基本数据类型和引用数据类型
如果是基本数据类型的变量,则数值一旦初始化后就不能更改。
如果是引用数据类型的变量,则在初始化后就不能让其指向另一个对象,但引用的值是可以变的。
public class FinalReferenceTest{
public static void main(){
final int[] iArr={1,2,3,4}; iArr[2]=-3;//合法
iArr=null;//非法,对iArr不能重新赋值
final Person p = new Person(25);
p.setAge(24);//合法
p=null;//非法
}
}
为什么局部内部类和匿名内部类只能访问final变量?
public class Test {
public static void main(String[] args) {
}
//局部final变量a,b
public void test(final int b) {//jdk8在这里做了优化, 不用写,语法糖,但实际上也是有的,也不能修改
final int a = 10; //匿名内部类
new Thread(){
public void run() {
System.out.println(a);
System.out.println(b);
};
}.start();
}
}
class OutClass {
private int age = 12;
public void outPrint(final int x) {
class InClass {
public void InPrint() {
System.out.println(x);
System.out.println(age);
}
}
new InClass().InPrint();
}
}
内部类和外部类是处在同一个级别的,内部类不会因为定义在方法中就随着方法的执行完毕而销毁。
这里就会产生一个问题:当外部类的方法执行完毕后,局部变量就会被销毁,但是内部对象可能存在对该局部变量的引用,此时就会产生一个矛盾:内部对象访问了一个不存在的变量。为了解决这个问题,就将局部变量复制一份作为内部变量的成员变量,这样当局部变量死亡后,内部类仍然可以访问它,实际访问的是局部变量的拷贝,变相延长了局部变量的生命周期。
将局部变量复制为内部类的成员变量时,必须保证这两个变量是一样的,也就是如果我们在内部类中修 改了成员变量,方法中的局部变量也得跟着改变,怎么解决问题呢?
就将局部变量设置为final,对它初始化后,我就不让你再去修改这个变量,就保证了内部类的成员变量和方法的局部变量的一致性。这实际上也是一种妥协。使得局部变量与内部类内建立的拷贝保持一致。
五、String、StringBuffer、StringBuilder
1、String 是final修饰的,不可变,因此每次操作都会产生新的String对象。
2、StringBuffer和StringBuilder的操作是在原对象上的。
3、StringBuffer是线程安全的(synchronized修饰),StringBuilder是线程不安全的。
性能方面:StringBuilder > StringBuffer > String
场景:经常需要改变字符串内容时使用后面两个
优先使用StringBuilder,多线程使用共享变量时使用StringBuffer
六、重载和重写的区别
重载: 发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问 修饰符可以不同,发生在编译时。 重写: 发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于 等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为private则子类就不能重写该方法(方法体不同)。
七、List和Set的区别
List:有序,按对象进入的顺序保存对象,可重复,允许多个null值存在,可以使用iterator取出所有元素,再逐一遍历,还可以使用get(int index)获取指定下标的元素。
Set:无序,不可重复,最多允许只有一个null值存在,取元素时只能使用iterator接口取得所有元素,再逐一遍历。
八、ArrayList和LinkedList区别
ArrayList:基于动态数组,连续内存存储,适合下标访问(随机访问),扩容机制:因为数组长度固定,超出长度存数据时需要新建数组,然后将老数组的数据拷贝到新数组,如果不是尾部插入数据还会涉及元素的移动(往后复制一份,插入新元素),使用尾插法并指定初始化容量可以极大提升性能、甚至超过LinkedList(需要创建大量的node对象)
LinkedList:基于链表,可以存储在分散的内存中,适合做数据插入和删除操作,不适合查询,需要逐一遍历
遍历LinkedList必须使用iterator不能使用for循环,因为每次for循环体内通过get(i)取得某一元素时都需要对list进行遍历,十分消耗性能。
另外不要试图使用indexOf等返回元素索引,并利用其进行遍历,使用indexlOf对list进行了遍历,当结果为空时会遍历整个列表。
九、HashMap和HashTable有什么区别?其底层实现是什么?
1、区别:
(1)HashMap方法没有synchronized修饰,非线程安全,HashTable线程安全。
(2)HashMap允许key和value为null,而HashTable不允许。
2.底层实现:数组+链表实现
jdk8开始链表高度到8、数组长度超过64,链表转变为红黑树,元素以内部类Node节点存在
计算key的hash值,二次hash然后对数组长度取模,对应到数组下标, 如果没有产生hash冲突(下标位置没有元素),则直接创建Node存入数组, 如果产生hash冲突,先进行equal比较,相同则取代该元素,不同,则判断链表高度插入链表,链 表高度达到8,并且数组长度到64则转变为红黑树,长度低于6则将红黑树转回链表 key为null,存在下标0的位置
数组扩容(类似于ArrayList扩容)
十、双亲委派机制
向上委派到顶层加载器为止,向下查找到发起加载的加载器为止(向上委派,向下查找)
双亲委派模型的好处:
主要是为了安全性,避免用户自己编写的类动态替换 Java的一些核心类,比如 String。
同时也避免了类的重复加载,因为 JVM中区分不同类,不仅仅是根据类名,相同的 class文件被不 同的 ClassLoader加载就是不同的两个类