散碎知识点
-
通过
HttpServletRequest. getParameter()
获取的参数编码格式由浏览器决定。
浏览器根据 html 中指定的编码格式进行编码,tomcat 根据指定的格式进行解码,tomcat 默认解码是 ISO-8859-1.
get 请求使用new String(username.getBytes("ISO-8859-1"), "UTF-8");
解决乱码;
post 请求使用request.setCharacterEncoding("utf-8");
-
for(;;)
和while(true)
都是无条件循环,使用 javac 编译后他们两个是一样的字节码. -
final 只是指向不变,但是指向的值有可能变,所以依然不是线程安全
-
包装类的
equals()
方法不处理数据转型,也就是说用 Integer 的 equals 比较 Long 类型,即使值相同也不返回 true -
调用
Object.wait()
会释放锁,获得执行权后会再尝试获取锁。 -
null 可以被强制类型转换成任意类型(不是任意类型对象),于是可以通过它来执行静态方法。
-
字符串常量池在 Java6 之前存放在方法区,而 Java7 中又把常量池移到了堆中(运行时常量池在方法区)
字符串本身是一个对象啊,对象在堆中,常量池里面放的只是一些引用,这些引用指向了堆中的具体的对象。 -
private 和 protected 修饰符不能修饰类。
一个类如果是私有的,其他都不能访问,那么它只能自己玩自己,没有意义。
一个类如果是受保护的,那么继承后可以访问,既然未继承之前是不可见的,那么也就无法进行继承,这样就显得毫无意义,不如直接用 default。 -
普通的初始化块 用于初始化非静态的属性;静态初始化块用于初始化静态属性,静态属性只有一份,也就是只会执行一次
class Test{
int i = 3;
// 等价于下面(会被翻译成下面的形式)
int i;
{
i = 3;
}
// 静态变量同理
static int a = 2;
// 等价于
static int a;
static {
a = 2;
}
// 一个例子
static {
x = 3;
System.out.println(x);
}
static int x = 2;
}
可以看到初始化操作时,还是会翻译成代码块的形式依次执行;在最后的一个例子中,打印语句会报错,JVM 不允许完全初始化之前使用变量,然后就是变量定义的初始化优先(语法优先),所以即使把 x 的定义放在下面也是可以编译运行的,顺序就成了:先执行定义语句 static int x;
然后依次开始执行 x = 3;x = 2;
最终 x 的值就是 2 了。
构造方法
是在创建对象的时候需要调用的方法 => 它是收尾的步骤。
注意:并不是用构造方法来创建对象,创建对象的过程是很复杂的,构造方法会在收尾时调用。
构造方法的修饰符默认和类名一致,构造方法的首行默认就是 super()
也就是调用父类的无参构造方法。
构造方法的首行 还可能出现 this()
,代表在执行当前的构造方法之前先去执行本类的其他构造方法。
由于 super() 和 this() 都必须出现在首行,所以它们无法同时存在,并且 super() 是默认值
构造方法是不可能覆盖的,因为它不会被继承,所以覆盖无从谈起。
参数传递
总结一下就是下面的两条总则:
- 基本数据类型传参赋值的时候 其实就是把值直接复制了一份
- 引用数据类型传引用的值 而引用的值就是一个内存指向的地址
然后来看下面的代码:
public class TestArgs3{
public static void main(String[] args){
String a = new String("O");
String b = new String("K");
change(a,b);
System.out.println(a);//?
System.out.println(b);//?
}
public static void change(String x,String y){
// 相当于是 String x = a;String y = b;
String temp = x;
x = y;
y = temp;
}
}
虽然 String 是引用数据类型,打印结果依然是 “OK”,a 和 b 的内存地址确实是赋给 x 和 y了,然后执行的交换操作是交换的局部变量!! 也就是 x 和 y 的地址确实改变了,但是和 a 、b 没有半毛钱关系。
然后来看个完整版:
public class TestArgsFinal{
public static void main(String[] args){
int num = 2;
change(num);
System.out.println(num);//2
Int ok = new Int(num);
change(ok);
System.out.println(ok.i);//3
changeRef(ok);
System.out.println(ok.i);//3
}
public static void change(int x){
x = 5;
}
public static void change(Int ia){
ia.i = 3;
}
public static void changeRef(Int ia){
ia = new Int(7);
}
}
class Int{
int i;
public Int(int i){
this.i = i;
}
}
也就是说,只有通过 .
属性的方式修改的,方法结束后才会保留,通过 new 的当方法结束,也就随之消亡了。
字符串
在给字符串赋值时,通过 ""
的话涉及到字符串常量池,new 不会涉及常量池。
当使用 ""
进行赋值时,内容会被收录到常量池当中,而当再次出现双引号直接赋值的时候,会进行常量池的过滤查找,如果已经出现过,则不会再分配新的空间,而直接指向原有(已经存在的)空间。
StringBuffer/StringBuilder 常用方法:
-
append()
-
insert()
在指定的下标插入内容,使用的其实是System.arraycopy()
来移动“数组”的。 -
reverse()
反转整个字符串
SB 中默认设置 16 个缓冲区,new 的时候可以手动进行指定。
String 其实就是个 char[]
,与 c 不同的是,末尾没有