前几天跟朋友讨论了几个问题,不算大事,都是一些细节方面的,但正是由于一些概念性的东西不清楚,导致无法做出判断,面试
题中也有可能会出现,我总结了一下,写了几个小代码加注释,方便自己以后查阅
1.字符编码问题
/*
* 我们知道Java中一个字符占2个字节,而我们又知道字符串是由字符系列组合而成,那我们
* 可不可以理解一个字符串的字节数 = 字符的个数(字符串的长度) * 2 呢?
* 下面的代码将证实这个问题
*/
public class CodeTest {
public static void main(String[] args) {
String ch = "A"; //等效于 char ch = 'A'
String str = "abc";
String strSub = "abc中国";
System.out.println(ch.getBytes().length);
System.out.println(str.getBytes().length);
System.out.println(strSub.getBytes().length);//此处打印结果为7,推测出字母占1字节,汉字占2字节,这个很像GB2312码表?
/*
* 上面的代码测试字符和字符串的字节数问题,但好像与我们所想的有差别,其实不然
* 我们都知道Java是采用Unicode码表的,到底是怎么一回事呢?
* 我们在写一个.java文件时,编译器编译成.class文件,这个过程是需要用到Unicode码表的
* 这时我们说char类型占2字节,字符串=字符*2,这样是成立的
* 但当JVM运行时,调用getBytes,这个是解码过程了,解码当然也需要一个码表了,这时候就默认当前系统的码表来解码了
* 这个问题曾今困惑我好长时间,今天终于知道了,虽然不是什么大问题,但也要了解额
*/
}
}
2.作用域的问题
其实这类问题以前还真没重视过,毕竟遇到的都是些小虾小蟹,直到前些天碰到一个面试题,才发觉有必要重视,代码如下:
public class StringBuilderTest {
public static void main(String[] args) {
StringBuilder sb1 = new StringBuilder("aaa");
StringBuilder sb2 = new StringBuilder("bbb");
run(sb1, sb2);
//下面的代码并不等效于run方法哦
//sb1.append("111");
//sb2 = sb1;
System.out.println(sb1);
System.out.println(sb2);
}
public static void run(StringBuilder sb1, StringBuilder sb2) {
sb1.append("111");
sb2 = sb1;
}
}
/*
* 这个是典型的作用域的问题
* 如上代码,当我们操作对象时(改变对象的内容),其作用域不会局限于方法体,而变量的作用域仅仅在方法内部
*/
3.继承的关系
在学面向对象的时候,其实还是有些概念性的东西没有弄清的,其实那也是必然,因为当时不知道自己究竟不知道什么,所以有些问
题就没有暴露出来,好在现在遇到了
/*
* 1.此程序是为了测试当子类重写了父类的方法(run)时,用父类类型的变量obj来指向new出的子类对象,
* 问obj调用的run方法是哪个类的
*
* 2.我们在父类的构造方法中调用一下run方法,那当我们new子类的对象时,问程序如何执行
*/
public class OverrideMehod {
public static void main(String[] args) {
A obj = new B(); //new出B的对象时调用父类的构造方法,根据打印结果,我们可以得出,此处是调用子类的
run方法
System.out.println("----------------------");
obj.run();//动态绑定,即当用父类类型的变量指向子类对象时,调用的run方法是子类的重写方法
System.out.println(obj.name);//静态绑定,即当用父类类型的变量指向子类对象时,调用的name成员变量是
父类的
//obj不可调用子类的runner方法
}
}
class A {
public String name = "A";
public A() {
run();
//根据前面的测试结果,我们可以很肯定的说出,当我们new子类对象时,此处的this代表的是子类的对象
System.out.println(this);
}
public void run() {
System.out.println("this is A");
}
}
class B extends A {
public String name = "B";
public B() {
super(); //此处的super是隐藏的
}
public void run() {
System.out.println("this is B");
}
public void runner() {
System.out.println("this is runner!!");
}
}
4.静态代码块和类加载机制的相关问题
static其他的一些用法我就不提了,在这主要是关于静态代码块的,父类和子类均有静态代码块,当new子类对象时,程序的执行顺
序如何,看代码证实了这个问题,看看注释
/* 类的加载机制:
* 加载一个类时,会优先去找父类的加载器,然后父类在去找父类的加载器,一级级向上寻找,如果super的加载器可以加载,
* 那么就加载,如果不行,就一级级向下推,直到此类本身,这就是Java中的委托机制
* 所以下面的代码会优先执行父类中的静态代码块
* 另外,当一个子类new对象时,"必然"会调用父类的构造方法,即在子类的构造方法中肯定含有super,不管是隐式还是显式的
* 如果是隐式的,即父类肯定含有隐式的构造方法,即无参
* 但如果父类只有一个有参的构造方法,子类也只有有参的构造方法(这时子类肯定不会有无参的构造方法,因为父类么有无
参,super()调用不了),
* 那么在子类的构造方法的第一行必须显式的调用父类的构造方法 即super(实参);
* 否则编译器会报错,如下代码可以试验一下
*
*/
public class StaticTest {
public static void main(String[] args) {
new BBB("111");
}
}
class AAA {
static {
System.out.println("this is AAA...");
}
public AAA() {
System.out.println("this is constructor of AAA...");
}
public AAA(String name) {
System.out.println("this is constructor of AAAA...");
}
}
class BBB extends AAA {
static {
System.out.println("this is BBB...");
}
public BBB() {
//super(); 隐藏的
System.out.println("this is constructor of BBB...");
}
public BBB(String name) {
System.out.println("this is constructor of BBBB...");
}
}