基础
1 jvm监控
在命令行输入 jvisualvm,即可打开
2 String
String 和 StringBuffer、StringBuilder 的区别是什么?String 为什么是不可变的?
- 可变性:String类中使用 final 关键字,所以不可变。 StringBuffer、StringBuilder 是可变的。
- 线程安全:String 不可变,线程安全。StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 线程不安全。
- 性能: String 类改变,生成新 String 对象,指针指向新的String。StringBuffer和StirngBuilder 对本身操作,而且StirngBuilder 比 StringBuffer 能有 10%~15% 提升,但有多线程不安全的风险。
new String()
首先明白创建了几个对象就是在说把对象放在了几个地方。那么一共就俩地方,一个是String池,一个是堆。然后开始各种String的测试。定义String,无非两种。1)String s1 = "hello";2)String s2 = new String("hello");第一种,会先验证String池中有没有"hello",有的话s1指向"hello"。没有的话创建新的"hello"存入String池中并指向"hello"。第二种,会先验证String池中有没有"hello",有的话对String池不做任何操作。没有的话创建新的"hello"并存入String池中。然后继续在堆里创建new String("hello"),并指向它。
3 拆箱和装箱
- 装箱:将基本类型用它们对应的引用类型包装起来。
- 拆箱:将包装类型转换为基本数据类型。
4 ==和equals
- ==:基本数据类型比较的是值,引用数据类型比较的是内存地址。
- equals:类没有覆盖 equals() 方法时比较的是内存地址。类覆盖了 equals() 方法时,一般都覆写成比较对象内容,类如String,内容相等则返回true
两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对?
这个本质在考 “x和y是两个对象,x.equals(y)是true的时候,x和y的hash code一样吗?”
答:不对,因为java中的规定是——1)如果两个对象相同(equals 方法返回 true),那么它们的hashCode 值一定要相同;2)如果两个对象的 hashCode 相同,它们并不一定相同。
关键就是搞清楚equals判断的是什么,看源码。
public boolean equals(Object obj) {
return (this == obj);
}
那么这里引出来 == 和 equals 的区别了。
如果是基本变量,根本没有equals方法,就是用 == ,比较的就是内容。如果是new出来的对象,父类是object的这种,==比较地址,equals也比较地址。如果是java中重写了equals的类,比较什么就看重写内容了。(一般重写都是为了比较内容)举两个例子。
1)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 (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
2)ArrayList的父类AbstractList的源码
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof List))
return false;
ListIterator<E> e1 = listIterator();
ListIterator e2 = ((List) o).listIterator();
while (e1.hasNext() && e2.hasNext()) {
E o1 = e1.next();
Object o2 = e2.next();
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
}
return !(e1.hasNext() || e2.hasNext());
}
所以,如果非要实现equals 方法返回 true,hashCode还不同的情况,也可以。重写equals方法。再举个例子。
public class Equals {
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof Equals) {
return true;
}
return false;
}
public static void main(String[] args) {
Equals A = new Equals();
Equals B = new Equals();
System.out.println(A.equals(B) == true);//true
System.out.println(A.hashCode());
System.out.println(B.hashCode());
}
}
5 值传递和引用传递
Java中只有值传递
- 传递基本类型参数:例如(int a),行参会创建副本,在函数内行参修改影响副本内容,但不会影响实参的内容。
- 传递引用类型参数:例如(int[] arr),这里传递的是实参的地址,既然传递的是地址,那么行参也指向实参的地址,所以修改行参值的时候实参也会改变。
当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?
先说结论:java中没有引用传递,只有值传递。所以这种情况也是值传递。
值传递的定义是:方法调用时,实际参数把它的值传递给对应的形式参数,方法执行中形式参数值的改变不影响实际参。引用传递的定义是:也称为传地址。方法调用时,实际参数的引用(地址,而不是参数的值)被传递给方法中相对应的形式参数,在方法执行中,对形式参数的操作实际上就是对实际参数的操作,方法执行中形式参数值的改变将会影响实际参数的值。
看了值传递的定义后,有了疑问。
问:“这种情况应该不是值传递吧?违反了值传递的定义呀,因为对象的属性变化了。”答:“对象属性改了没问题,但是传递的参数并不是对象呀,所以它改不改不违反值传递。”问:“传递的参数不是对象,那是啥?”答:“是对象的引用的一个副本。”问:“这不还是引用吗?为啥不是引用传递?”答:“还是看定义,引用传递 直接传地址。这种情况传的引用,指向地址。所以这是通过传递对象引用副本的方式实现了引用传递的效果,但是,它是值传递。”
问:“确定是值传递的问题可以了,那String对象作为参数传递的话,为什么没有返回变化后的结果呢?”答:“额,那照这么说,Integer,Double等也会有此疑问是吧?”问:“是的。”答:“因为,Integer是int的包装类,Double是double的包装类,String是char[]的包装类。对包装类的值操作实际上是通过对其对用的基本类型操作实现的。”
char型变量中能不能存储一个中文汉字
char类型是用来存储Unicode编码的字符,unicode字符集中包含了汉字。只不过一些特殊的汉字没有被包含在字符集里。
unicode编码占用两个字节,char类型变量也是占用两个字节。
6 final
- 修饰基本数据类型变量:数值在初始化后不能更改。
- 修饰引用类型变量:初始化后不能指向其他对象。
- 修饰一个类:类不能被继承,成员变量隐形指定为final。
7 修饰符
作用域 | 当前类 | 同包 | 子类 | 其他 |
---|---|---|---|---|
public | ✅ | ✅ | ✅ | ✅ |
protected | ✅ | ✅ | ✅ | ❌ |
default | ✅ | ✅ | ❌ | ❌ |
private | ✅ | ❌ | ❌ | ❌ |
8 成员变量和局部变量
成员变量不需要显式赋值,局部变量需要显式赋值,否则编译通过不了,为什么
- 成员变量不确定什么就会被取值出来用,还有可能在各个方法中是不同的值
- 显然局部变量非要有默认值也可以设计出来,不这么设计是因为:局部变量的作用就是在局部方法中某个地方取出来用。在一个方法体内赋值和取值的顺序是固定,先赋值,才能取值。没必要有默认值。
9 System.exit
在一个if-else判断中,如果我们程序是按照我们预想的执行,到最后我们需要停止程序,那么我们使用System.exit(0),而System.exit(1)一般放在catch块中,当捕获到异常,需要停止程序,我们使用System.exit(1)。这个status=1是用来表示这个程序是非正常退出。
10 blob和clob和string
// 由于UTF-8是多字节编码,需要用多个字节来表示一个字符的编码,所以也就出现了在转换之后byte[]数组长度、内容不一致的情况。
// 而ISO-8859-1编码是单字节编码
String s1="test";
Clob c = new SerialClob(s1.toCharArray());//String 转 clob
Blob b = new SerialBlob(s1.getBytes(StandardCharsets.ISO_8859_1));//String 转 blob
//也可以这样不传字符集名称,默认使用系统的
//Blob b = new SerialBlob(s1.getBytes());
String clobString = c.getSubString(1, (int) c.length());//clob 转 String
String blobString = new String(b.getBytes(1, (int) b.length()),StandardCharsets.ISO_8859_1);//blob 转 String
//前面若没传入字符集名称,则这里也不需要传入,以免出错
//String blobString = new String(b.getBytes(1, (int) b.length()));
System.out.println(clobString);
System.out.println(blobString);
11 float f=3.4;是否正确?
不正确,java内,整数默认是int,浮点默认是double,支持向上转型,即int自动转long,float自动转double,但不支持自动向下转型。float f = (float)3.4; float f= 3.4f;都是对的。
short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗?
第一个有错,还是那句话,整数java默认是int。s1是short,1是int,相加,向上转型是int,int不能赋值给short,需要强转。s1 = (short) s1 + 1;第二个没错,s1 += 1;其实就是s1 = (short) s1 + 1;
13 堆,栈,静态区的分配?
heap区:(存储的基本单位)
1.保存对象的实例(new创建的对象和数组),实例属性,属性类型,对象本身的类型标记。2.存储的对象包含一个与之对应的class信息。3.JVM只有一个heap区,被所有的线程共享。4.一般人为释放,否则程序结束时由OS回收。
stack区:(运行的基本单位)
1.stack区存对象的引用,和基础数据类型。2.保存一个4个字节的heap内存地址,用来定位该对象引用的实例在heap区的位置。3.每个线程都有一个stack区,互相之间不能访问。
静态区/方法区:
1.被所有线程共享。2.包含所有class和static变量。3.初始化的全局变量和初始化的静态变量在一个区域,未初始化的全局变量和未初始化的静态变量在一个区域
14 2乘以8?
位运算:System.out.println(2<<3);即可。其实就是2乘(2的三次方)2的二进制往左移两位。2的二进制0010,移动之后,1000。换成十进制就是16。
15 i-=i+=i-=i+=i-=i--
这类题要记住两点,1、i的值都不会在这串运算中改变。2、从右往左算。
public static void main(String[] args) {
int i = 5;
//i=i-i+i-i+i-(i-1)
//5-5+5-5+5-4
System.out.println(i-=i+=i-=i+=i-=--i);//1
i = 5;
//i=i-i+i*i+i-(i-1)
//5-5+5*5+5-4
System.out.println(i-=i+=i*=i+=i-=--i);//-30
}
16 类初始化顺序
1 无继承关系
- static成员变量和static块(static成员变量和static块两者与前后顺序有关)
- 普通成员变量和非static块(普通成员变量和非static块与前后顺序有关)
- 构造函数
package test;
public class Father {
public static String staticField = printStaticField();
public String field = printField();
static {
System.out.println("【静态代码块】");
}
{
System.out.println("【非静态代码块】");
}
public static String printStaticField() {
System.out.println("【静态变量】");
return "静态变量";
}
public String printField() {
System.out.println("【非静态变量】");
return "非静态变量";
}
public Father() {
System.out.println("【构造方法代码块】");
}
public static void main(String[] args) {
new Father();
}
}
/*
结果:
【静态变量】
【静态代码块】
【非静态变量】
【非静态代码块】
【构造方法代码块】
*/
2 有继承关系
- 静态初始化——父类static成员变量和父类static块(两者之间与前后顺序有关)
- 静态初始化——子类static成员变量和子类static块(两者之间与前后顺序有关)
- 父类初始化——父类普通成员变量和父类非static块(两者之间与前后顺序有关)
- 父类初始化——父类构造函数
- 子类初始化——子类普通成员变量和子类非static块(两者之间与前后顺序有关)
- 子类初始化——子类构造函数
2.1 父类
package test;
public class Father {
public static String staticField = printStaticField();
public String field = printField();
static {
System.out.println("【父类-静态代码块】");
}
{
System.out.println("【父类-非静态代码块】");
}
public static String printStaticField() {
System.out.println("【父类-静态变量】");
return "父类-静态变量";
}
public String printField() {
System.out.println("【父类-非静态变量】");
return "父类-非静态变量";
}
public Father() {
System.out.println("【父类-构造方法代码块】");
show();
}
public void show() {
System.out.println("【父类-普通方法】");
}
}
2.2 子类
package test;
public class Son extends Father {
public static String staticField = printStaticField();
public String field = printField();
static {
System.out.println("【子类-静态代码块】");
}
{
System.out.println("【子类-非静态代码块】");
}
public static String printStaticField() {
System.out.println("【子类-静态变量】");
return "子类-静态变量";
}
public String printField() {
System.out.println("【子类-非静态变量】");
return "子类-非静态变量";
}
public Son() {
System.out.println("【子类-构造方法代码块】");
show();
}
public void show() {
System.out.println("【子类-普通方法】");
}
public static void main(String[] args) {
new Son();
}
}
输出(换行是我人为换行,为了方便观察):
【父类-静态变量】//只有第一次加载时执行
【父类-静态代码块】//只有第一次加载时执行
【子类-静态变量】//只有第一次加载时执行
【子类-静态代码块】//只有第一次加载时执行
【子类-非静态变量】//这里出现 子类 的原因是:子类方法被父类方法 覆盖
【父类-非静态代码块】
【父类-构造方法代码块】
【子类-普通方法】//这里出现 子类 的原因是:子类方法被父类方法 覆盖
【子类-非静态变量】
【子类-非静态代码块】
【子类-构造方法代码块】
【子类-普通方法】
在这里增加一个**继承关系里的方法覆盖知识点。**如果不想让父类方法被覆盖,那么可以用 在父类方法上加 final 关键字,但是加了关键字之后,子类中根本不允许存在和父类方法名相同的方法(方法参数一致的情况)。 那么还可以用 private 关键字,嗯,可以实现了。