前言
2022年了各行各业都不容易,互联网行业更是卷得没法看,但还是那句话,在没有能力制定规则之前最好老老实实遵守规则,这会让你活得更轻松一些,。
今天也不搞那些花里胡哨的了,单纯的总结了一下今年面试被问得最多的1000道题,说1000道就是1000道,一题都不少,希望对还没找到合适工作的同学有所帮助。
除了这些之外我个人也整理了一些还不错的学习资料。
资料获取方式:关注小编+私信【面试题】获取上述资料~
面向对象
什么是面向对象?
对比面向过程,是两种不同的处理问题的角度面向过程更注重事情的每一个步骤及顺序,面向对象更注重事情有哪些参与者(对象)、及各自需要做什么
比如:洗衣机洗衣服面向过程会将任务拆解成一系列的步骤(函数),1、打开洗衣机----->2、放衣服----->3、放洗衣粉----->4、清洗 >5、烘干面向对象会拆出人和洗衣机两个对象: 人:打开洗衣机 放衣服 放 洗 衣 粉 洗衣机:清洗 烘干从以上例子能看出,面向过程比较直接高效,而面向对象更易于复用、扩展和维护
面向对象
封装:封装的意义,在于明确标识出允许外部使用的所有成员函数和数据项内部细节对外部调用透明,外部调用无需修改或者关心内部实现
1、javabean的属性私有,提供getset对外访问,因为属性的赋值或者获取逻辑只能由javabean本身决 定。而不能由外部胡乱修改该name有自己的命名规则,明显不能由外部直接赋值
private String name;
public void setName(String name){ this.name = "tuling_"+name;
}
2、orm框架
操作数据库,我们不需要关心链接是如何建立的、sql是如何执行的,只需要引入mybatis,调方法即可。
继承:继承基类的方法,并做出自己的改变和/或扩展子类共性的方法或者属性直接使用父类的,而不需要自己再定义,只需扩展自己个性化的。
多态:基于对象所属类的不同,外部对同一个方法的调用,实际执行的逻辑不同。 继承,方法重写,父类引用指向子类对象,父类类型 变量名 = new 子类对象 ;变量名.方法名();无法调用子类特有的功能。
JDK JRE JVM
JDK:
Java Develpment Kit java 开发工具
JRE:
Java Runtime Environment java运行时环境JVM:
java Virtual Machine java 虚拟机
==和equals比较
==对比的是栈中的值,基本数据类型是变量值,引用类型是堆中内存对象的地址
equals:object中默认也是采用==比较,通常会重写Object
public boolean equals(Object obj) { return (this == obj);
}
String
上述代码可以看出,String类中被复写的equals()方法其实是比较两个字符串的内容。
public boolean equals(Object anObject) { if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject; int n = value.length;
if (n == anotherString.value.length) { char v1[] = value;
char v2[] = anotherString.value; int i = 0;
while (n-- != 0) {
if (v1i != v2i) return false;
i++;
}
return true;
}
}
return false;
}
public class StringDemo {
public static void main(String args[]) { String str1 = "Hello";
String str2 = new String("Hello"); String str3 = str2; // 引用传递
System.out.println(str1 == str2); // false System.out.println(str1 == str3); // false System.out.println(str2 == str3); // true System.out.println(str1.equals(str2)); // true System.out.println(str1.equals(str3)); // true System.out.println(str2.equals(str3)); // true
}
}
hashCode与equals
hashCode介绍:
hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,Java中的任何类都包含有hashCode() 函数。
散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用 到了散列码!(可以快速找到所需要的对象)
为什么要有hashCode:
以“HashSet如何检查重复”为例子来说明为什么要有hashCode:
对象加入HashSet时,HashSet会先计算对象的hashcode值来判断对象加入的位置,看该位置是否有 值,如果没有、HashSet会假设对象没有重复出现。但是如果发现有值,这时会调用equals()方法来 检查两个对象是否真的相同。如果两者相同,HashSet就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。这样就大大减少了equals的次数,相应就大大提高了执行速度。
如果两个对象相等,则hashcode一定也是相同的两个对象相等,对两个对象分别调用equals方法都返回true 两个对象有相同的hashcode值,它们也不一定是相等的因此,equals方法被覆盖过,则hashCode方法也必须被覆盖hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个 对象无论如何都不会相等(即使这两个对象指向相同的数据)
final
最终的
修饰类:表示类不可被继承
修饰方法:表示方法不可被子类覆盖,但是可以重载修饰变量:表示变量一旦被赋值就不可以更改它的值。
- 修饰成员变量
如果final修饰的是类变量,只能在静态初始化块中指定初始值或者声明该类变量时指定初始值。如果final修饰的是成员变量,可以在非静态初始化块、声明该变量或者构造器中执行初始值。
- 修饰局部变量
系统不会为局部变量进行初始化,局部变量必须由程序员显示初始化。因此使用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; 但是不允许第二次赋值
}
}
- 修饰基本类型数据和引用类型数据
如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。但是引用的值是可变 的。
public class FinalReferenceTest{ public static void main(){
final int[] iArr={1,2,3,4}; iArr2=-3;//合法
iArr=null;//非法,对iArr不能重新赋值
final Person p = new Person(25);
p.setAge(24);//合法
p=null;//非法
}
}
为什么局部内部类和匿名内部类只能访问局部final变量?
编译之后会生成两个class文件,Test.class Test1.class
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();
}
}
首先需要知道的一点是: 内部类和外部类是处于同一个级别的,内部类不会因为定义在方法中就会随着方法的执行完毕就被销毁。
这里就会产生问题:当外部类的方法结束时,局部变量就会被销毁了,但是内部类对象可能还存在(只有 没有人再引用它时,才会死亡)。这里就出现了一个矛盾:内部类对象访问了一个不存在的变量。为了解 决这个问题,就将局部变量复制了一份作为内部类的成员变量,这样当局部变量死亡后,内部类仍可以 访问它,实际访问的是局部变量的"copy"。这样就好像延长了局部变量的生命周期将局部变量复制为内部类的成员变量时,必须保证这两个变量是一样的,也就是如果我们在内部类中修 改了成员变量,方法中的局部变量也得跟着改变,怎么解决问题呢?
就将局部变量设置为final,对它初始化后,我就不让你再去修改这个变量,就保证了内部类的成员变量和方法的局部变量的一致性。这实际上也是一种妥协。使得局部变量与内部类内建立的拷贝保持一致。
String、StringBuffer、StringBuilder
String是final修饰的,不可变,每次操作都会产生新的String对象StringBuffer和Stri