文章目录
错题一
1、静态方法本身属于类,因此可以通过类名直接调用;
2、静态代码块只在类加载时执行一次;
3、static块和static方法中不能出现this或者super关键字;
4、引用静态方法时,可以用类名.方法名
或者对象名.方法名
的形式,但是不可以用this.方法名
的形式;
错题二
这里看了一位网友的解答:
观察可以发现,每次进入循环之后,x本身都在以
x^2
的速度进行增长,也就是进入循环t次,最终的x就变成了2^t
,只有当x>=n/2
的时候,才会跳出循环,最终计算得出:时间复杂度答案为A;
错题三
- 采用命令行
java Test one two three
调用,其中Test为调用的方法,而one two three
则为Test方法里面 main 函数的参数;
错题四
1、在Java中,变量有两种类型,一种是原始类型,一种是引用类型。
2、原始类型一共有8种,它们分别是char,boolean,byte,short,int,long,float,double
;在Java API中,有它们对应的包装类,分别是(首字母大写)Character,Boolean,Byte,Short,Integer,Long,Float,Double
;
3、JAVA JVM
对于不同的原始类型会分配不同的存储空间,具体分配如下:
byte: 1个字节 8位 127 (有符号)
short:2个字节 16位 32767
int: 4个字节 32位 2147483647
long: 8个字节 64位 9223372036854775807
float:4个字节 32位 3.4028235E38
double:8个字节 64位 1.7976931348623157E308
枚举(enum)类型是Java 5新增的特性,它是一种新的类型,允许用常量来表示特定的数据片断,而且全部都以类型安全的形式来表示,是特殊的类,可以拥有成员变量和方法。
错题五
【线程私有】
1、程序计数器:用于指示当前线程所执行的节码执行到第几行;每个线程都有一个;
2、虚拟机栈:一个线程的每个方法在执行的时候都会创造一个栈帧,存储局部变量表,操作站,动态链接,方法入口,当每个方法被调用的时候,栈帧入栈,方法执行完后,栈帧出栈。
3、本地方法栈:本地方法栈在作用,运行机制,异常类型等方面和虚拟机栈相同,区别是:虚拟机栈执行的是Java方法,而本地方法栈执行native方法,
【线程共享】
1、堆区:堆区是用来存储对象实例。
2、方法区:方法区是线程共享,用于存储已经被虚拟机加载的类信息,包括版本,field,方法,接口等信息,final常量,静态变量,编译器及时编译的代码等。方法区上执行垃圾回收很少,所以方法区也被称为永久代
。
错题六
来自一位网友的评论:
package java.lang;
public class Object {
/* 一个本地方法,具体是用C(C++)在DLL中实现的,然后通过JNI调用。*/
private static native void registerNatives();
/* 对象初始化时自动调用此方法*/
static {
registerNatives();
}
/* 返回此 Object 的运行时类。*/
public final native Class<?> getClass();
/*
hashCode 的常规协定是:(本质 上是 返回该对象的哈希码值。 )
1.在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
2.如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。
3.如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法不 要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。
*/
public native int hashCode();
public boolean equals(Object obj) {
return ( this == obj);
}
/*本地CLONE方法,用于对象的复制。*/
protected native Object clone() throws CloneNotSupportedException;
/*返回该对象的字符串表示。非常重要的方法*/
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
/*唤醒在此对象监视器上等待的单个线程。*/
public final native void notify();
/*唤醒在此对象监视器上等待的所有线程。*/
public final native void notifyAll();
/*在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。换句话说,此方法的行为就好像它仅执行 wait(0) 调用一样。
当前线程必须拥有此对象监视器。该线程发布对此监视器的所有权并等待,直到其他线程通过调用 notify 方法,或 notifyAll 方法通知在此对象的监视器上等待的线程醒来。然后该线程将等到重新获得对监视器的所有权后才能继续执行。*/
public final void wait() throws InterruptedException {
wait( 0 );
}
/*在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。*/
public final native void wait( long timeout) throws InterruptedException;
/* 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量前,导致当前线程等待。*/
public final void wait( long timeout, int nanos) throws InterruptedException {
if (timeout < 0 ) {
throw new IllegalArgumentException( "timeout value is negative" );
}
if (nanos < 0 || nanos > 999999 ) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range" );
}
if (nanos >= 500000 || (nanos != 0 && timeout == 0 )) {
timeout++;
}
wait(timeout);
}
/*当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。*/
protected void finalize() throws Throwable { }
}
错题七
- What will be printed when you execute the following code?
class C {
C() {
System.out.print("C");
}
}
class A {
C c = new C();
A() {
this("A");
System.out.print("A");
}
A(String s) {
System.out.print(s);
}
}
class Test extends A {
Test() {
super("B");
System.out.print("B");
}
public static void main(String[] args) {
new Test();
}
}
- 初始化过程是这样的:
1、首先,初始化父类中的静态成员变量和静态代码块,按照在程序中出现的顺序初始化;
2、然后,初始化子类中的静态成员变量和静态代码块,按照在程序中出现的顺序初始化;
3、其次,初始化父类的普通成员变量和代码块,在执行父类的构造方法;
4、最后,初始化子类的普通成员变量和代码块,在执行子类的构造方法;
(1)初始化父类的普通成员变量和代码块,执行
C c = new C();
输出C;
(2)super("B");
表示调用父类的构造方法,不调用父类的无参构造函数,输出B;
(3)System.out.print("B");
所以输出CBB
;
错题八
public static String toString(char c) { return String.valueOf(c); }
public static String valueOf(char c) { char data[] = {c}; return new String(data, true); }
返回的都是字符串,只有char变成 int 的时候才会变为对应的assic码;
错题九
- 被static修饰的变量称为静态变量,静态变量属于整个类,而局部变量属于方法,只在该方法内有效,所以static不能修饰局部变量;
错题十
- StringBuilder、StringBuffer、String 都是 final 的,但是为什么StringBuilder、StringBuffer可以进行修改呢,因为不可变包括的是,引用不可变以及对象不可变,而这三个都是属于引用不可变,(也就是地址不要变,里面的内容随心所欲),而StringBuilder , StringBuffer 中都包含右append方法,可对对象中的内容进行增加。
- 而
String a="123"+new String("456");
实际上底层是用了一个StringBuffer 进行append;
错题十一
1、基本型和基本型封装型进行“==”
运算符的比较,基本型封装型将会自动拆箱变为基本型后再进行比较,因此Integer(0)会自动拆箱为int类型再进行比较,显然返回true;
2、两个Integer类型进行“==”
比较,如果其值在-128至127,那么返回true,否则返回false, 这跟Integer.valueOf()的缓冲对象有关,这里不进行赘述。
3、两个基本型的封装型进行equals()比较,首先equals()会比较类型,如果类型相同,则继续比较值,如果值也相同,返回true;
4、基本型封装类型调用equals()
,但是参数是基本类型,这时候,先会进行自动装箱,基本型转换为其封装类型,再进行3中的比较;
错题十二
- 由于
replaceAll()
的第一个参数是一个正则表达式,而"."
在正则表达式中表示任何字符,所以会把前面字符串的所有字符都替换成"/"
。如果想替换的只是"."
,那么久要写成"\\."
;
错题十三
- Iterator接口本身被Conllection接口继承;
错题十四
- 访问修饰符的限制可以等于重写方法的访问修饰符;
错题十五
1、sleep()方法
在指定时间内让当前正在执行的线程暂停执行,但不会释放“锁标志”
。不推荐使用。sleep()使当前线程进入阻塞状态,在指定时间内不会执行。
2、wait()方法
在其他线程调用对象的notify或notifyAll方法前,导致当前线程等待。线程会释放掉它所占有的“锁标志”,从而使别的线程有机会抢占该锁。当前线程必须拥有当前对象锁。如果当前线程不是此锁的拥有者,会抛出IllegalMonitorStateException
异常。唤醒当前对象锁的等待线程使用notify或notifyAll方法,也必须拥有相同的对象锁,否则也会抛出IllegalMonitorStateException
异常。waite()和notify()必须在synchronized函数或synchronized block中进行调用。如果在non-synchronized函数或non-synchronized block中进行调用,虽然能编译通过,但在运行时会发生IllegalMonitorStateException
的异常。
3、yield方法
暂停当前正在执行的线程对象。yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。yield()只能使同优先级或更高优先级的线程有执行的机会。
4、join方法
等待该线程终止。等待调用join方法的线程结束,再继续执行。如:t.join();
:主要用于等待t线程运行结束,若无此句,main则会执行完毕,导致结果不可预测。
错题十六
【volatile的措施】
在JVM中,有主内存和工作内存的概念,每个线程对应一个工作内存,并共享主内存数据;
1、对于普通变量:读操作会优先读取工作内存的数据,如果工作内存不存在,则从主内存中拷贝一份数据到工作内存,写操作只会修改工作内存中的副本数据,这种情况下,其他线程就无法读取变量的最新值。
2、对于volatile变量:读操作时JVM会把工作内存中对应的值设置为无效,要求线程从主内存中读取数据,写操作JVM也会把工作内存中对应的数据刷新到主内存中,这种情况下,其他线程就可以读取变量的最新值。
3、volatile变量的内存可见性,是基于内存屏蔽实现的,内存屏蔽也就是一个CPU指令。在程序运行的时候,为了提高执行性能,编译器和处理器会对指令进行重排序,JVM为了保证不同的编译器和CPU上有相同的结果,通过插入特定类型的内存屏蔽来禁止特定类型的编译器重排序和处理器重排序,插入一条内存屏蔽会告诉编译器和CPU,不管什么指令都不能和这条内存屏蔽指令重排序。
4、处理器为了提高处理速度,不直接和内存进行通讯,而是将系统内存的数据独到内部缓存后再进行操作,但操作完后不知什么时候会写到内存。
5、如果对声明了volatile变量进行写操作时,JVM会向处理器发送一条Lock前缀的指令,将这个变量所在缓存行的数据写会到系统内存。 这一步确保了如果有其他线程对声明了volatile变量进行修改,则立即更新主内存中数据。
6、但这时候其他处理器的缓存还是旧的,所以在多处理器环境下,为了保证各个处理器缓存一致,每个处理会通过嗅探在总线上传播的数据来检查自己的缓存是否过期,当处理器发现自己缓存行对应的内存地址被修改了,就会将当前处理器的缓存行设置成无效状态,当处理器要对这个数据进行修改操作时,会强制重新从系统内存把数据读到处理器缓存里。 这一步确保了其他线程获得的声明了volatile变量都是从主内存中获取最新的。
【总结】:
- volatile只保证了可见性和防止了指令重排序,并没有保证原子性。
- volatile修饰的变量只是保证了每次读取时都从主存中读,每次修改时,都将修改后的值重新写入了主存。
- 在synchronized修饰的方法体或者常量(final)不需要使用volatile。
- 由于使用了volatile屏蔽掉了JVM中必要的代码优化,所以在效率上比较低,因此一定在必要的时候才能使用该关键字。
错题十七
1、HashMap的基类是AbstractMap,而基接口是Map;Hashtable的基类是Dictionary,基接口是Map;
2、HashMap和Hashtable的初始容量都是11,负载因子是075;但是扩容机制不同,HashMap是旧数组长度*2
;Hashtable是2*旧表长度+1
;
3、HashMap是非线程安全的,Hashtable是线程安全的,因为所有的方法都使用了synchronized;
4、HashMap使用迭代器迭代,Hashtable使用迭代器和枚举迭代;
5、HashMap中的key和value都可以是null,Hashtable中都不能是Null;
6、HashMap中取消了contains(),使用了containsKey和containsValue,而Hashtable中3个方法都有;
7、Hashtable:使用key的hashCode()作为hash值,和数组长度进行求余运算,得到键值对在数组中的位置,然后再使用equals()形成链表;
HashMap:使用key的hashCode()进行高低16位&运算作为hash值,和数组长度-1进行&运算。得到键值对在数组中的位置,然后再使用equals()形成链表;
错题十八
- 在Java中,对象的内存在哪个时刻回收,取决于垃圾回收器何时运行;
- 一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize(),并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存;
- 在C++中,对象的内存是在哪一时刻被回收,是可以确定的,析构函数和资源的释放息息相关,能不能正确处理析构函数,关乎能否正确回收对象内存资源;
- 在Java中,对象的内存在哪一时刻被回收,取决于垃圾回收器何时运行;finalize()的作用是用来回收"本地方法"中的本地对象;
- 线程在run()执行完以后就会释放掉内存,但其引用不一定不存在了;
错题十九
在调用子类构造器之前,会先调用父类构造器,当子类构造器中没有使用"super(参数或无参数)"指定调用父类构造器时,默认使用父类的无参构造;
如果父类中包含有参构造,却没有无参构造,则在子类构造器中一定显式的使用"super(参数)指定父类的有参构造,不然就会报错;
错题二十
- abstract类本身也是一个类,它本身可以存在构造函数,但是不能创建对象,它的构造存在的意义是子类初始化时调用;
错题二十一
1、Java中所有错误和异常的父类是java.lang.Throwable;
2、基本数据类型不是对象,不能用new的方法获取,但是每个基本数据类型都对应着封装类型,这些封装类型为了解决基本数据类型面向对象用的。
3、垃圾回收的线程优先级相当低,即使垃圾回收器工作,finalize()也不一定得到执行,这是由于程序中的其他线程的优先级远远高于执行finalize()函数线程的优先级。或者说,如果是等待清理队列中如果又被调用,则不会执行finallize()。所以说:Java通过垃圾回收回收不再引用的变量,垃圾回收时对象的finallize()不一定会得到执行。
4、Java跨平台是因为有JVM的存在,Java的三个版本的运行需要各自不同的环境。
错题二十二
【Exception(异常)】
- 是程序本身可以处理的异常,主要包含RuntimeException等运行时异常和IOException、SQLException等非运行时异常。
- 运行时异常包括:都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
- 运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。
- 非运行时异常(编译异常) 包括:RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常;
错题二十三
- 数值型变量在默认情况下为int型,byte和short型在计算时会自动转换为int型计算,结果也是int 型。所以a1*a2的结果是int型的。
- short类型转为byte类型,需要标明强制转换;
错题二十四
- 开放定址法(open addressing): 假如你在外面旅游时,吃坏东西,急需上洗手间,当你好不容易找到一件洗手间的时候,发现排了好多人,这时你会怎么做? 如果是链接法,排队不就行了,我就在外面等,迟早会排到我的;
- 如果是开放定址法:直接放弃现有洗手间,去寻找新的洗手间,也就是放弃已被占用的位置,寻找新的插入位置就是开放定址法的思想,开放定址法中的开放二字指的是没有被占用的位置,定址指的是确定位置。开放定址法中,所有的元素都放在散列表中(链接法放在链表中)。
- 也就是说散列表中的每一个位置,要么有元素,要么没有元素。当需要删除,查询元素时,我们从某一个位置开始,按照某种特定的确定下一个位置的方法来检查所有表项,直到找到目标元素,或者没有找到。
错题二十五
- Servlet的生命周期可以分为初始化阶段,运行阶段和销毁阶段三个阶段,以下过程属于初始化阶段是
- 加载Servlet类及.class对应的数据
- 创建ServletConfig对象
- 创建Servlet对象
- 每一次请求来到容器时,会产生
HttpServletRequest
与HttpServlceResponse
对象,并在调用service()方法时当做参数传入。在WEB容器启动后,会读取Servlet设置信息,将Servlet类加载并实例化,并为每个Servlet设置信息产生一个ServletConfig
对象,而后调用Servlet接口的init()方法,并将产生的ServletConfig
对象当作参数传入。
错题二十六
- Iterator 支持从源集合中安全地删除对象,只需在 Iterator 上调用 remove() 即可。这样做的好处是可以避免 ConcurrentModifiedException ,当打开 Iterator 迭代集合时,同时又在对集合进行修改。有些集合不允许在迭代时删除或添加元素,但是调用 Iterator 的remove() 方法是个安全的做法。
- 直接使用iterator进行remove就没有问题;或者不使用iterator进行遍历,使用list的size方法和get方法结合进行遍历;
错题二十七
错题二十八
public class NameList
{
private List names = new ArrayList();
public synchronized void add(String name)
{
names.add(name);
}
public synchronized void printAll() {
for (int i = 0; i < names.size(); i++)
{
System.out.print(names.get(i) + ””);
}
}
public static void main(String[]args)
{
final NameList sl = new NameList();
for (int i = 0; i < 2; i++)
{
new Thread()
{
public void run()
{
sl.add(“A”);
sl.add(“B”);
sl.add(“C”);
sl.printAll();
}
} .start();
}
}
}
-
首先,集合s1是不可变的;
-
然后正常情况下,第一次线程t1运行,添加并打印ABC,然后执行第二个线程添加ABC,此时s1中就有两个ABC了,打印出ABCABC. 结果就是ABCABCABC。
-
但是由于线程之间执行顺序的不确定性,可以在t1添加完ABC后,要执行打印之前,t2执行了一次(可以是一次到三次)添加操作A,那么此时s1中就有ABCA 打印出的也就是ABCA;
-
然后t1之行结束,t2继续执行添加BC.然后s1变成ABCABC。所以打印出ABCABC,总的结果就是
ABCA ABCABC
;
【总结】:
第一次打印的长度不可变,但是在3~6之间;
第二次打印集合中一定是6个元素,长度也就是6;