7-12日
解析:
输出到控制台,直接’System.out.println()’
out是“标准”输出流,Public static final PrintStream out
out是PrintStream类型,PrintStream是包装流,你传入什么,他就输出什么
解析:
在数据库操作中,为了有效保证并发读取数据的正确性,提出的事务隔离级别;为了解决更新丢失,脏读,不可重读(包括虚读和幻读)等问题,在标准SQL规范中,定义了4个事务隔离级别:
未授权读取(read uncommitted)
授权读取(read committed)
可重复读取(repeatable read)
序列化(serializable)
解析:
HashMap在单线程中使用大大提高了,在多线程的情况下使用hashTable来确保安全。hashTable中使用synchronized关键字来实现安全机制,但是synchronized是对整张hash表进行锁定即让线程独享整张hash表,在安全同时造成了浪费concurrentHashMap采用分段加锁的机制来确保安全。
Array.asList()将一个数组转化为一个List对象,这个方法会返回一个ArrayList类型的对象,这个ArrayList类并非java.util.ArrayList类,而是Array类的静态内部类,用这个对象对咧进行CRUD操作,就会报UnsupportedOperationException异常
ConcurrentHashMap使用segment来分段和管理锁,segment继承自ReentrantLock,因此ConcurrentHashMap使用ReentrantLock来保证线程安全。
解析:
start()方法来启动线程,真正实现了多线程运行,调用run()方法,run()方法当作普通方法的方式调用
CyclicBarrier让一组线程等待其它线程;CountDownLatch让一组线程等待某个事件发生
Callable能够抛出check exception
start()方法让thread进去可运行状态(runnable),等待获取CPU的使用权限权。
CountDownLatch:允许一个或多个线程等待其它线程完成操作
CyclicBarrier:(同步屏障)让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会执行。
7-13
解析:
java.io.Serializable接口是一个标志性接口,在接口内部没有定义任何属性与方法,只是用于标志此接口的实现类可以被序列化与反序列化
java.lang.Cloneable接口是一个标志性接口,在接口内部没有定义任何属性与方法。以指示Object.clone方法可以合法地对该类实例进行按字段复制
java.lang.CharSequence接口对许多不同种类的char序列提供统一的只读访问接口。charSequence是char值得一个可读序列
java.lang.Comparable接口,此接口强制对实现它得每个类得对象进行整体排序,此序列被称为该类的自然排序
解析:
引用代表引用的是实际的对象,对引用的修改就是对对象的修改,可以理解成两把钥匙可以打开同一扇门
引用传参保存的是一个地址,这个地址里保存的是变量的具体值,而引用类型作为参数的时候,是将变量保存的地址值赋值到参数变量里,这样它们都指向了同一个内容,这样改变参数的成员变量的话,那么相应的变量的成员也会改变。
解析:
Thread.sleep()和Object wait()都可以抛出InterruptException。这个异常是不能忽略的,因为他是一个检查异常(check exception)
check exception:指的是编译时异常,该类异常需要本函数处理,用try和catch处理,或者throws抛出异常,然后交给调用者去处理
runtime exception:指的是运行时异常,该类异常不必须本函数处理,当然也可以处理
public class Test {
public static void main(String[] args) {
System.out.println("return value of getValue(): " +
getValue());
}
public static int getValue() {
try {
return 0;
} finally {
return 1;
}
}
}
根据官方JVM规范:
如果try语句里有return,返回的是try语句块中变量值
详细过程如下:
如果try,finally语句里均有return,忽略try的return,而使用finally的return
finally语句总是要执行的,不管出没出现异常,当finally语句中有return时,会覆盖try/catch语句块的return,所以一般不要在finally中加return语句。
return的两个作用:返回数据,结束方法运行。
解析:
Swing是在AWT的基础上构建的一套新的图形界面系统,它提供了AWT所能够提供的所有功能,并且用纯粹的Java代码对AWT的功能进行了大幅度的扩充,AWT是基于本地方法的C/C++程序,其运行速度比较快,Swing是基于AWT的Java程序,其运行速度比较慢
解析:
类的复用有两种方式:组成(has-a)和继承(is-a)
继承:指一个新的类继承原有类的基本特性,并增加了新的特性(Java不允许多继承,而C++可以)
多态性:指允许不同类的对象对同一消息做出响应,即同一消息可以根据发送对象的不同而采用多种不同的行为方式
多态存在的三个必要条件:
实现多态性的三种形式:
多态的分类:
多态分为编译时多态和运行时多态,其中编译时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的函数,通过编译之后会变成两个不同的函数,在运行时谈不上多态。
而运行时多态是动态的,它是通过动态绑定来实现的,也就是我们平常所说的多态性。
关于方法重载:方法重载是指在一个类中定义多个同名的方法,但要求每个方法具有不同的参数的类型或参数的个数。调用重载方法时,Java编译器能通过检查调用的方法的参数类型和个数选择一个恰当的方法,方法重载通常用于创建完成一组任务相似但参数的类型或参数的个数不同的方法。
方法重载的具体规范:
方法区在JVM中也是一个非常重要的区域,它与堆一样,是被线程共享的的区域。在方法区中,存储了每个类的信息(包括类的名称,方法信息,字段信息),静态变量,常量以及编译器编译后的代码等。
概括来说,JVM初始运行的时候都会分配好Method Area(方法区)和Heap(堆),而JVM每遇到一个线程,就会为其分配一个Program Counter Register(程序计数器),VM Stack(虚拟机栈)和Native Method Stack(本地方法栈),当线程终止时,三者(虚拟机栈,本地方法栈和程序计数器)所占用的内存空间也会被释放掉。这也是为什么把内存区域分为线程共享和非线程共享的原因,非线程共享的那三个区域的生命周期与所属线程相同,而线程共享的区域与JAVA程序运行的生命周期相同,所以这也是系统垃圾回收的场所只发生在线程共享的区域(实际上对大部分虚拟机来说只发生在Heap上)的原因。
方法区存放了所加载的类的信息(名称,修饰符等),类中的静态变量,类中定义为final类型的常量,类中的Field信息,类中的方法信息,当开发人员在程序中通过Class对象中的getName,isInterface等方法来获取信息时,这些数据都来源于方法区域,同时方法区域也是全局共享的,在一定的条件下它也会被GC,当方法区域需要使用的内存超过其允许的大小时,会抛出OutOfMemory的错误信息。
解析:
s为null,因此只要调用了s.length()都会抛出空指针异常,因此这个题目主要考察if语句的后半部分会不会执行。
A:单个‘与’操作的符号&用在整数上时按位与,用在布尔型变量上跟&&功能类似,但是区别是无论前面是否为真,后面必定执行,因此抛出异常
B:‘与’操作,前半部分判断为假,后面不再执行
C:这里跟&和&&的区别类似,后面必定执行,因此抛出异常
D:‘或’语句,前面为真,整个结果必定为真,后面不执行
7-17
解析:
D:捕获到的异常不仅可以在当前方法中处理,还可以将异常抛给调用它的上一级方法来处理
A:运行时异常Java虚拟机会自动处理,只有当是检查时异常Java会强制性抛出异常,对异常进行处理。
解析:
abstract修饰符用来修饰类和成员方法
1.用abstract修饰的类表示抽象类,抽象类位于继承树的抽象层,抽象类不能被实例化
2.用abstract修饰的方法表示抽象方法,抽象方法没有方法体。抽象方法用来描述系统具有什么功能,但不提供具体的实现。
Abstract是Java中的一个重要关键字,可以用来修饰一个类或者一个方法,修饰一个方法时,表示该方法只有特征签名,没有具体实现,而是把具体实现留给继承该类的子类,一个类中只要有一个abstract方法,那么这个类就要被声明为abstract,但是其中可以有非abstract方法。abstract类可以使得类得设计者能够创建方法的原型,而真正的实现留给使用这个类的人。
字段是指:成员变量和成员常量
Java
//point X
public class Foo {
public static void main(String[] args) throws Exception {
PrintWriter out = new PrintWriter(
new java.io.OutputStreamWriter(System.out), true);
out.printIn(“Hello”);
}
}
解析:
Java中没有include关键字,导包用import
在创建OutputStreamWriter的时候,使用的是类的全名称,所以不需要使用import
解析:
解析:
不可变类(Immutable Class)是一旦被实例化就不会改变自身状态(或值)的类
String就是一种典型的不可变类
不可变类的主要用途是在多线程环境下确保对象的线程安全
不可变类一般建议使用final来修饰(比如String就是final类),否则子类可以通过继承不可变类的方式,增加setter方法,从而改变对象的状态,破坏了不可变的约束
解析:
StringBuffer s = new StringBuffer(x);X为初始化容量长度。s.append(“Y”);“Y”表示长度为y的字符串;length始终返回当前长度即y;
对于s.capacity():
1.当y < x时,值为x
以下情况容器容量需要扩充
1.当 x < y < 2*x+2时,值为2*x+2
2.当y > 2*x+2时,值为y
解析:
A:形式参数可被视为local variable,形参和局部变量一样都不能离开方法,都只有在方法内部才会发生作用,不会在方法外可见
B:对于形式参数只能用final修饰符,其他任何修饰符都会引起编译器错误,但是这个修饰符也有一定的限制,就是在方法中不能对参数做任何修改。不过一般情况下,一个方法的形参不用final修饰,只有在特殊情况下,那就是:方法内部类,一个方法内的内部类如果使用了这个方法的参数或者局部变量的话,这和参数或局部变量应该是final
C:形参的值在调用时根据调用者更改,实参则用自身的值更改形参的值(指针,引用皆在此列),也就是说真正被传递的是实参。
D:方法的参数列表指定要传递给方法什么样的信息,采用的都是对象的形式。因此,在参数列表中必须指定每个所传递对象的类型及名字。像Java中任何传递对象的场合一样,这里传递的实际上也是引用,并且引用的类型必须正确
public class Test2
{
public void add(Byte b)
{
b = b++;
}
public void test()
{
Byte a = 127;
Byte b = 127;
add(++a);
System.out.print(a + " ");
add(b);
System.out.print(b + "");
}
}
解析:
包装类的值都是final不可变的,对于++b或者b++,只是新创建了一个对象,然后把引用传给了原对象句柄,在函数中操作,只是形参的临时句柄改变了指向
7-18
抛出InterruptedException的代表方法有:
说下CyclicBarrier是一个屏障类,它的await方法可以简单的理解为:等待多个线程同时到达之后才能继续进行,在此之前它就是这些线程的屏障,线程不能继续进行,而对于失败的同步尝试,CyclicBarrier使用了一种要么全部要么全不(all-or-none)的破坏模式:因为中断,失败或者超时等原因,导致线程过早的离开了屏障点,那么在该屏障点等待的其它所有线程也将通过BrokenBarrierException(如果它们几乎同时被中断,则用interruptedException)以反常的方式离开。因此它被中断也是可以抛出interruptedException的
解析:
\0是空字符
A:不存在x[25] ,索引从0开始到length-1
B:x[24]存的是默认值0(Java中没有“\0”这一说)
C:超出内存 正确
D:第二元素
解析:
同一个类内,private变量可以访问,由于x是static的,存储在类内,而不是对象内,所以++,–操作的是同一个变量
类变量即可以通过类来返回,也可以通过类的对象来访问。但通过类的对象来访问类变量时,实际不是访问该对象所拥有的变量,因为当系统创建该类的对象时,系统不会再为类变量分配内存,也不会再次对类变量进行初始化。也就是说,对象根本不拥有对应类的类变量,通过对象访问类变量只是一种假象,通过对象访问的依然是该类的类变量。
class Person {
String name = "No name";
public Person(String nm) {
name = nm;
}
}
class Employee extends Person {
String empID = "0000";
public Employee(String id) {
empID = id;
}
}
public class Test {
public static void main(String args[]) {
Employee e = new Employee("123");
System.out.println(e.empID);
}
}
解析:
父类没有无参的构造函数,所以子类需要在自己的构造函数中显式的调用父类的构造函数
添加super(“nm”),否则报错
class Person {
String name = "No name";
public Person(String nm) {
name = nm;
}
}
class Employee extends Person {
public Employee(String nm) {
super(nm);
// TODO Auto-generated constructor stub
}
String empID = "0000";
}
public class Test {
public static void main(String args[]) {
Employee e = new Employee("123");
System.out.println(e.empID);
}
}
也就是:如果子类构造器没有显示地调用超类的构造器,则将自动地调用超类默认(没有参数)的构造器。如果超类没有不带参数的构造器,并且在子类的构造器中没有显式地调用超类的其它构造器,则Java编译器将报错。
使用super调用构造器的语句必须是子类构造器的第一条语句
——p153《Java核心技术卷I》
class A{
public A foo(){return this;}
}
class B extends A{
public A foo(){
return this;
}
}
class C extends B {
_______
}
子类重写父类方法遵循“两同两小一大”的规则
“两同”:方法名相同,形参列表相同
“两小”:子类方法返回值类型应比父类方法返回值类型更小或相等,子类方法声明抛出的异常类应比父类方法声明抛出的异常类更小或相同
“一大”:子类方法的访问权限应比父类方法的访问权限更大或相等
注意:覆盖方法和被覆盖方法要么都是类方法,要么都是实例方法,不能一个是类方法一个是实例方法
class HelloA{
public HelloA()
{
System.out.println("I’m A class ");
}
static
{
System.out.println("static A");
}
}
public class HelloB extends HelloA{
public HelloB()
{
System.out.println("I’m B class");
}
static{
System.out.println("static B");
}
public static void main (String[] args){
new HelloB();
}
}
解析:静态优先,父类优先
类的初始化顺序是:
解析:
A:类方法是指类中被static修饰的方法,无this指针
C:类方法是可以调用其它类的static方法的
D:可以在类方法中生成实例对象再调用实例方法,太绝对了,在类中申请一个类对象或者参数传递一个对象或指针都可以调用
成员方法又称为实例方法
静态方法又称为类方法
7-20
解析:
private方法只可以在类的内部使用,在类外部根本访问不到;而final方法可以在类外部访问,但是不可以重写该方法。
就是说可以使用该方法的功能但是不可以改变其功能
解析:
CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里,比如缓存
ReadWriteLock当写操作时,其他线程无法读取或写入数据,而当读操作时,其它线程无法写入数据,但却可以读取数据。适用于读取远远大于写入的操作
ConcurrentHashMap是一个线程安全的Hash Table,它的主要功能是提供了一组和HashTable功能相同但是线程安全的方法。
Hash Table是对整个代码块加锁,而ConcurrentHashMap是使用分片锁,粒度较小,不用对整个代码块加锁,提高了读写速率;准确说Hash Map底层是数组+链表的结构,它的线程是不安全的,而Hash Table是在Hash Map的基础上,对整个哈希表(数组)加锁sychronized实现线程安全,而我们知道,方法访问数组中的数据,可能只涉及到数组的部分,对整个数组加锁会降低线程并发执行的效率,所以如果对数组分段加锁,使用segment分片锁,这样一个线程只会锁住数组的一片,其它线程仍可以访问数组的其它片进行写操作,具有这样的分片锁的机制就是**ConcurrentHashMap**
Volatile只能保证变量的安全,而不能保证线程的安全
解析:
Java8的接口方法可以有如下定义:
public,abstract,default,static,strictfp
解析:
Java回收机制是自发的,无需人为操作,finalize是这种机制执行时需要调用的函数,可以认为更改
在Java虚拟机的垃圾回收器看来,堆区中的每个对象都可能处于以下三个状态之一:
notify唤醒线程,与wait()和同步锁synchronized()方法配合使用
Java中整型默认是int
浮点默认是double
解析:
出于运行速率的考虑,Java编译器会把经常访问的变量放到缓存(严格说应该是工作内存)中,读取变量则从缓存中读。但是在多线程编程中,内存中的值和缓存中的值可能会不一样。volatile用于限定变量只能从内存中读取,保证保证对所有线程而言,值都是一样的。但是volatile不能保证原子性,也就不能保证线程安全。
Java中volatile关键字的功能:
volatile是Java中的一个类型修饰符,它是被设计用来修饰被不同线程访问和修改的变量。如果不加入volatile,基本上会导致这样的结果:要么无法编写多线程程序,要么编译器失去大量优化的机会。
public class Test{
public String name="abc";
public static void main(String[] args){
Test test=new Test();
Test testB=new Test();
System.out.println(test.equals(testB)+","+test.name.equals(testB.name));
}
}
解析:
没有重写equals时,是直接用==判断的,而String中重写了equals方法。
简单说:比较地址值,但是String重写了,所以可以比较内容
总结:如果finally块中有return语句的话,它将覆盖掉函数中其它return语句。
7-21
解析:
能够对对象进行传输的只有ObjectOutputStream和ObjectInputStream这些以Object开头的流对象
transient修饰的变量在对象串化的时候并不会将所附的值保存到串中,串行化的对象从磁盘读取出来仍然是Null。即transient代表对象的临时数据
解析:
运行时异常顾名思义就是程序在运行的时候出现异常,隐含的一个前提就是程序在编译时是检测不到一场的存在的,作者本人也并不知道是否自己的代码中含有运行时异常,所以根本也不可能提前使用try{}catch语句所捕获
鲁棒性:即健壮性
程序设计语言中,数组元素在内存中是一个接着一个线性存放的,通过第一个元素就能访问随后的元素,这样的数组称之为“真数组”
解析:
属于面向接口编程,是Java语言的一大优点,ArrayList虽然是一个具体的类,按照ArrayList myList = new ArrayList;的确可以生成一个myList对象,而且编译也不会报错。但实际开发中不采用这样的方式即实际开发时都是 :接口名 xxx = new 接口某实现类()。这样便于:
子父类存在同名成员时,子类中默认访问子类的成员,可通过super指定访问父类的成员,格式:super.xx
创建子类对象时,默认会调用父类的无参构造器,可通过super指定调用父类其它构造器,格式:super(yy)
解析:
新生代:
老年代:当Survivor区也满了之后就通过Minor GC将对象复制到老年代。老年代也满了的话,就将触发Full GC,针对整个堆(包括新生代,老年代,持久代)进行垃圾回收
7-22
byte b1=1,b2=2,b3,b6,b8;
final byte b4=4,b5=6,b7;
b3=(b1+b2); /*语句1*/
b6=b4+b5; /*语句2*/
b8=(b1+b4); /*语句3*/
b7=(b2+b5); /*语句4*/
System.out.println(b3+b6);
解析:
Java表达式转型规则由低到高转换:
语句1:b3=(b1+b2);自动转为int,所以正确写法为b3=(byte)(b1+b2);或者将b3定义为int;
语句2:b6=b4+b5;b4、b5为final类型不会自动提升,所以和的类型视左边变量类型而定,即b6可以试任何数值类型
语句三:b8=(b1+b4);虽然b4不会自动提升,但b1仍会自动提升,所以结果需要强转,b8=(byte)(b1+b4);
语句四:b7=(b2+b5);同上。同时注意b7是final修饰,即只可以赋值一次,便不可再改变
public class Test {
public int aMethod(){
static int i = 0;
i++;
return i;
}
public static void main(String args[]){
Test test = new Test();
test.aMethod();
int j = test.aMethod();
System.out.println(j);
}
}
解析:
静态变量只能在类主体中定义,不能在方法中定义。
静态变量属于类所有而不属于方法。
Java子类重写继承的方法时,不可以降低方法的访问权限,子类继承父类的访问修饰符要比父类的更大,也就是更开放,假如我父类是protected修饰的,其子类只能是protected或者public,绝不能是friendly(默认访问范围)或者private,当然使用private就不是继承了。还要注意的是,继承当中子类抛出的异常必须是父类抛出的异常的子类,或者子类抛出的异常要比父类抛出的异常要少
解析:
假设利用return语句从try语句块中退出,在方法返回前,finally子句的内容将被执行。如果finally子句中也有一个return语句,这个返回值将会覆盖原始的返回值。
public static void main(String[] args) {
int k = f_test();
System.out.println(k);
}
public static int f_test(){
int a = 0;
try{
a = 1;
return a;
}
finally{
System.out.println("It is in final chunk.");
a = 2;
return a;
}
}
输出:
it is in final chunk
2
如果将return a;注释掉,将输出:
it is in final chunk
1
解析:
创建泛型对象的时候,一定要指出类型变量T的具体类型。争取让编译器检查出错误,而不是留给JVM运行的时候抛出类不匹配的异常。
事实上,JVM并不知道泛型,所有的泛型在编译阶段就已经被处理成了普通类和方法,处理的方法就叫做类型变量T的擦除。
总结:
如果类没有构造方法,JVM会生成一个默认构造方法,如果定义了任意类型的构造方法,编译器都不会自动生成构造方法
解析:
在Java源文件中,import语句应位于package语句之后,所有类的定义之前,可以没有,也可以有多条,package语句必须放在第一行
B:因为其根目录和子目录下可能有同名类,若都能读取,则会混淆
Java中的多线程是一种抢占式的机制,而不是分时机制。抢占式的机制是有多个线程处于可运行状态,但是只有一个线程在运行
共同点:
不同点:
7-23
解析:
A:是抢占,不是终止
B:是终止
C:是暂停,不是终止
D:和A差不多
Integer i01 = 59;
int i02 = 59;
Integer i03 =Integer.valueOf(59);
Integer i04 = new Integer(59);
解析:
Integer i01 = 59的时候,会调用Integer的valueOf方法
public static Integer valueOf(int i) {
assert IntegerCache.high>= 127;
if (i >= IntegerCache.low&& i <= IntegerCache.high)
return IntegerCache.cache[i+ (-IntegerCache.low)];
return new Integer(i); }
这个方法就是返回一个Integer对象,只是在返回之前,做了一个判断,判断当前i的值是否在[-128,127]区间,且IntegerCache中是否存在此对象,如果存在,则直接返回引用,否则,创建一个新对象。
在这里的话,因为程序初次运行,没有59,所以,直接返回一个新的对象
int i02=59,这是一个基本类型,存储在栈中
Integer i03 = Integer.valueOf(59),因为IntegerCache中已经存在此对象,所以,直接返回引用。
Integer i04=new Integer(59);直接创建一个新的对象
A:System. out .println(i01== i02);i01是Integer对象,i02是int,这里比较的不是地址,而是值。Integer会自动拆箱成int,然后进行值得比较,所以,为真。
B:System. out .println(i01== i03); 因为i03返回的是i01的引用,所以,为真。
C:System. out .println(i03==i04); 因为i04是重新创建的对象,所以i03,i04是指向不同的对象,所以,比较结果,为假。
D:System. out .println(i02== i04); 因为i02是基本类型,所以此时i04会自动拆箱,进行值比较,所以,为真。
public class Base
{
private String baseName = "base";
public Base()
{
callName();
}
public void callName()
{
System. out. println(baseName);
}
static class Sub extends Base
{
private String baseName = "sub";
public void callName()
{
System. out. println (baseName) ;
}
}
public static void main(String[] args)
{
Base b = new Sub();
}
}
解析:
new Sub();在创造派生类的过程中首先创建基类对象,然后才能创建派生类。
创建基类即默认调用Base()方法,在方法中调用callName()方法,由于派生类中存在此方法,则被调用的callName()方法是派生类中的方法,此时派生类还未构造,所以变量baseName的值为Null。
子类中定义了同名的baseName,会隐藏父类的baseName,所以这里输出的是子类的baseName,这时子类的额baseName还没有初始化。如果用super.baseName会打印出base。
解析:构造方法不能被子类继承,所以用final修饰没有意义。构造方法用于创建一个新的对象,不能作为类的静态方法,所以用static修饰没有意义
解析:一个对象只要实现了Serilizable接口,这个对象就可以被序列化,Java的这种序列化模式为开发者提供了很多便利,我们可以不必关心序列化的过程,只要这个类实现了Serilizable接口,这个类所有属性和方法都会自动序列化。
这个类的有些属性需要序列化,有些不需要被序列化
Java的transient关键字提供了便利。你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中
简单说:
解析:两种方法的区别:
start方法:
用start方法来启动线程,是真正实现了多线程,通过调用Thread类的start()方法来启动一个线程,此时这个线程就处于就绪状态(可运行)状态,并没有运行,一旦得到CPU时间片,就开始执行run()方法,但要注意的是,此时无需等待run()方法执行完毕,即可继续执行下面的代码。所以run()方法并没有实现多线程。
run方法:
run方法只是类的一个普通方法而已,如果直接调用run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可以继续执行下面的代码。
解析:
A:Java编译成的是字节码,再被各系统的jvm编译成本系统可以识别的机器码,这就是jvm一次编程多平台应用的跨平台性。
B:Java源文件生成的是class文件,与系统无关
C:注意字节码和机器码不是一回事,Java程序在运行时字节码才会被jvm翻译成机器码,所以说Java是解释性语言
D:注意JVM的版本
JVM堆内存分为两块:Permanent Space和Heap Space
年轻代:
所有新生成的对象首先都是放在年轻代。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。年轻代一般分为3个区,1个Eden区,两个Survivor区(from和to)
大部分对象在Eden区生成。当Eden区满时,还存活的的对象将复制到Survivor区(两个中的各一个),当一个Survivor区满时,此区的存活对象将被复制到另一个Survivor区,当另一个Survivor区也满了的时候,从前一个Survivor区复制过来的并且此时还存活的对象,将可能被复制到年老代。
2个Survivor区是对称的,没有先后关系,所以同一个Survivor区中可能同时存在从Eden区中复制过来的对象,和从另一个Survivor区复制过来的对象;而复制到年老代的只有从另一个Survivor区过来的对象。而且,因为需要交换的原因,Survivor区至少有一个是空的。特殊情况下,根据需要Survivor区可以配置为多个的(多于2个),这样可以增加对象在年轻代中的存在时间,减少被放到年老代的可能
针对年轻代的垃圾回收即Young GC
年老代:
在年轻代中经历了N次(可配置)GC后仍然存活的对象,就会被复制到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象
针对年老代的垃圾回收即Full GC
持久代:
用于存放静态类型数据。如Java Class,Method等,持久代对GC没有显著影响。但是有些应用可能动态生成或调用一些Class,例如Hibernate CGLib等,在这种时候往往需要设置一个比较大的持久代空间来存放这些运行过程中动态增加的类型
所以当一组对象生成时,内存申请过程如下:
OOM(Out of Memory)异常一般有如下两种原因:
年老代溢出,表现为:java.lang.OutOfMemoryError:Javaheapspace
这是最常见的情况,产生的原因可能是:设置的内存参数Xmx过小或者程序的内存泄漏及使用不当问题。
例如循环上万次的字符串处理,创建上千万个对象,在一段代码内申请上百M甚至上G的内存。
还有的时候虽然不会报内存溢出,却会使系统不间断的GC,也无法处理其他请求。这种情况下除了检查程序,打印堆内存的方法排查,还可以借助一些内存分析工具,比如MAT。
持久代溢出,表现为java.lang.OutOfMemoryError:PermGenspace
通常由于持久代设置过小,动态加载了大量Java类而导致溢出
解决方法唯有将参数 -XX:MaxPermSize 调大(一般256m能满足绝大多数应用程序需求)。将部分Java类放到容器共享区(例如Tomcat share lib )去加载的方法也是一个思路,但前提是容器里部署了多个应用,且这些应用有大量共享类库
7-24
Collection:(只能存储引用类型,而且是单个存储)
List(有序,可重复)
Set (无序,不可重复)不允许有相同的元素
Map:(键无序不可重复)
解析:
“进程的区”属于操作系统里面的
一条进程的栈区,堆区,数据区和代码区在内存中的映射
注意:
一条进程在内存中的映射:
假设现在有一个程序,它的函数调用顺序如下:
main()->func_1->func_2->func_3,即:主函数main调用函数func_1;函数func_1调用func_2;函数func_2调用func_3
当一个程序被操作系统调入内存运行,其对应的进程在内存中的映射,如下
Iterator it = list.iterator();
int index = 0;
while (it.hasNext())
{
Object obj = it.next();
if (needDelete(obj)) //needDelete返回boolean,决定是否要删除
{
//todo delete
}
index ++;
}
解析:
Iterator支持从源集合中安全的删除对象,只需在Iterator上调用remove()即可。这样做的好处是可以避免ConcurrentModifiedException,当打开Iterator迭代集合时,同时又在对集合进行修改。有些集合不允许在迭代时删除或添加元素,但是调用Iterator的remove()方法是个安全的做法
解析:
运行时数据区包括:虚拟机栈区,堆区,方法区,本地方法栈,程序计数器
Math.ceil:天花板数,向上取整
Math.floor:地板数,向下取整
解析:
A:一个文件中,可以有多个public class
public class Main { public class Inner{
}
}
即外部类为public,还可以有Public的内部类
B:一个文件中可以有多个类,可以是多个并列的类,也可以是外部类,内部类结合
C:一个类中,可以有多个main方法,这是重载。但是public static void main(String[] str)
的方法只能有一个。
D:类中,可以有main方法,也可以没有main方法,而有一个main()方法的时候,也可以是任意访问权限。因为这个类不一定要执行,可以只是辅助类
解析:
A:省略构造函数,编译器会自动生成
D:构造函数可以重载
B:方法是可以和类名同名的,和构造方法唯一的区别就是,构造方法没有返回值。如下代码:
public class TestConStructor
{
public TestConStructor()
{
System.out.println("constructor");
}
public void TestConStructor()
{
System.out.println("not constructor");
}
public static void main(String[] args)
{
TestConStructor testConStructor = new TestConStructor();
System.out.println("main");
testConStructor.TestConStructor();
}
解析:
A:文件分为文本文件和二进制文件,计算机只认识二进制,所以实际上都是二进制的不同解释方式。
B:File类是对文件整体或者文件属性操作的类,例如创建文件,删除文件,查看文件是否存在等功能,不能操作文件内容;文件内容是用IO流操作的
C:当输入过程中意外到达文件或流的末尾时,抛出EOFException异常。正常情况下读取到文件末尾时,返回一个特殊值表示文件读取完成,例如read()返回-1表示文件读取完成
D:参照A,不论是文本文件还是二进制文件,在计算机中都是以二进制形式存储的,所以都会当作二进制文件读取。
Java的关键字对Java的编译器有特殊的意义,他们用来表示一种数据类型,或者表示程序的结构等。关键字不能用作变量名,方法名,类名,包名和参数
true和false是boolean的变量值,是编译器赋予特定含义的,但并不是关键字。
public static void main(String args[]) {
List Listlist1 = new ArrayList();
Listlist1.add(0);
List Listlist2 = Listlist1;
System.out.println(Listlist1.get(0) instanceof Integer);
System.out.println(Listlist2.get(0) instanceof Integer);
}
解析:
collection类型的集合(Array List,Linked List)只能装入对象类型的数据,该题中装入了0,是一个基本类型,但是JDK5以后提供了自动装箱与自动拆箱,所以int类型自动装箱变成为Integer类型,编译能够正常通过。
将list1的引用赋值给list2,那么list1和list2都将指向同一个堆内存空间。instanceof是Java关键字,用于判断一个对象是否属于某个特定类的实例,并且返回boolean类型的返回值。显然,list1.get(0)和list2.get(0)都属于Integer的实例。
public class Test
{
public static void main(String[] args)
{
int x = 0;
int y = 0;
int k = 0;
for (int z = 0; z < 5; z++) {
if ((++x > 2) && (++y > 2) && (k++ > 2))
{
x++;
++y;
k++;
}
}
System.out.println(x + ”” +y + ”” +k);
}
}
解析:
八进制,以8为基数,逢8进1.所以8在八进制就是010,前面的0是为了和十进制区分,也叫转译符
7-25
String s1=new String( ” xyz ” );
String s2=new String( ” xyz ” );
Boolean b1=s1.equals(s2);
Boolean b2=(s1==s2);
System .out.print(b1+ ” ” +b2);
解析:
String s1 = new String( ” xyz ”);//创建String类型的内容为xyz的s1对象
String s2 = new String( ” xyz ”);//创建String类型的内容为xyz的s2对象
Boolean b1 = s1.equals(s2);//比较s1对象和s2对象的内容相等,返回true
Boolean b2 = (s1==s2);//比较s1和s2两个对象的存储地址是否相等,明显两者分别存储在不同的地址,false。
byte[] src,dst;
解析:
先解码在编码
用new String(src,”GBK”)解码得到字符串
用getBytes(“UTF-8”)得到UTF-8编码字节数组
解析:
匿名内部类的创建格式为:
new 父类构造器(参数列表)|实现接口(){
//匿名内部类的类体实现
}
解析:
A:Java的访问权限有public,protected,private和default,default不能修饰变量
C:普通变量不能用abstract修饰,abstract一般修饰方法和类
D:被定义为abstract的类需要被子类继承,但是被修饰为final的类是不能被继承和改写的
native修饰方法简单说就是:一个Java方法调用了一个非Java代码的接口。定义native方法时,并不提供实现体,因为其实现体使用非Java语言在外面实现的。native可以和任何修饰符连用,abstract除外,因为native暗示这个方法是有实现体的,而abstract却显示指明了这个方法没有实现体
class Person {
String name = "No name";
public Person(String nm) {
name = nm;
}
}
class Employee extends Person {
String empID = "0000";
public Employee(String id) {
empID = id;
}
}
public class Test {
public static void main(String args[]) {
Employee e = new Employee("123");
System.out.println(e.empID);
}
}
解析:
父类没有无参
的构造函数,所以子类需要在自己的构造函数中显示调用父类的构造函数。
添加super("nm")
否则报错
简单说:子类的构造方法总是先调用父类的构造方法,如果子类的构造方法没有明显地指明使用父类的哪个构造方法,子类就调用父类不带参数的构造方法。
而父类没有无参的构造函数,所以子类需要在自己的构造函数中显示的调用父类的构造函数
解析:
ArrayList的构造函数总共有三个:
ArrayList的默认长度为10个,所以如果你往list里添加20个元素肯定要扩容两次(扩充一次是1.5倍,容量是15,还得扩充一次,也就是扩充两次)
解析:
Java多态有两种情况:重载和覆写
在覆写中,运用的是动态单分配,是根据new的类型确定对象,从而确定调用的方法
在重载中,运用的是静态单分配,根据静态类型确当对象,因此不是根据new的类型确定调用的方法。
普通方法:运用的是动态单分配,是根据new的类型确定对象,从而确定调用的方法
静态方法:运用的是静态多分派,即根据静态类型确定对象,因此不是根据New的类型确定调用的方法
方法重载是指在一个类中定义多个同名的方法,但要求每个方法具有不同的参数类型或参数个数,原则如下:
解析:
标识符由数字,字母和下划线,美元符号组成。在Java中是区分大小写的,而且还要求首位不能是数字。最重要的是,Java关键字不能当作Java标识符。
7-26
解析:
B:String类没有append()方法
C:引用类型只有String可以直接赋值,其他的都要New出来
D:StringBuffer类可以直接改它的内容,不用重新分配地址;String对象/实例,是不可以被改变的。
String:是对象不是原始类型。为不可变对象,一旦被创建,就不能修改它的值。对于已经存在的String对象的修改都是重新创建一个新的对象,然后把新的值保存进去。String是final类,即不能被继承
StringBuffer:
是一个可变对象,当对他进行修改的时候不会像String那样重新建立对象。他只能通过构造函数来建立,StringBuffer sb = new StringBuffer();
不能通过赋值符号对他进行赋值。sb = "welcome wo china
//error
对象被建立以后,在内存中就会分配内存空间,并初始保存一个null,向StringBuffer中赋值的时候可以通过它的append()方法sb.append("hello");
字符串连接操作中StringBuffer的效率比String高
checked exception:指的是编译时异常,该类异常需要本函数必须处理的,用try和catch处理,或者用throws抛出异常,然后交给调用者去处理异常
runtime exception:指的是运行时异常,该类异常不必须本函数处理,当然也可以处理
解析:
抽象类有构造方法,但是不能new一个对象
public>protected>default(同包)>private
简单说:数组命名时名称与【】可以随意排列,但声明的二维数组中第一个中括号中必须要有值,它代表的是在该二维数组中有多少个一维数组
JDK8开始,接口中可以定义有方法体的方法,方法必须被default和Static修饰。除此之外,其他方法都是抽象方法
package test;
import java.util.Date;
public class SuperTest extends Date{
private static final long serialVersionUID = 1L;
private void test(){
System.out.println(super.getClass().getName());
}
public static void main(String[]args){
new SuperTest().test();
}
}
解析:
TestSuper和Data的getClass都没有重写,它们都是调用Object的getClass,而Object的getClass作用是返回的是运行时的类的名字。这个运行时的类就是当前类,所以
super.getClass().getName()
返回的是test.SuperTest,与Data类无关
要返回Data类的名字需要写super.getClass().getSuperclass()
getName()
:包名+类名
7-27
解析:
char 2字节,short 2字节,float 4字节,double 8字节
往精度高的转
低级向高级是隐式类型转换,高级向低级必须强制类型转换
byte < char < short < int < long < float < double
解析:
A:static成员变量是在类加载的时候生成的
B:static成员函数即可以通过类名直接调用,也可以通过对象名进行调用
C:虚函数是C++中的,虚函数不可能是static
D:static成员函数可以访问static成员变量
解析:
A:throws关键字可以在方法上声明该方法要抛出的异常,然后在方法内部通过throw抛出异常对象
B:throw用于抛出异常
public class A implements B{
public static void main(String args[]){
int i;
A a1=new A();
i =a1.k;
System.out.println("i="+i);
}
}
interface B{
int k=10;
}
解析:
在接口里面的变量默认都是public static final的,他们都是公共的,静态的,最终的常量。相当于全局变量,可以直接省略修饰符。实现类可以直接访问接口中的变量。
都是Throwable的子类:
解析:
线程的启动方式只能通过start这种方式启动才能真正的实现多线程,如果是手动调用run方法和普通的方法调用没有区别,所以这个还是按照顺序执行首先执行run方法之后,执行输出语句,所以最终得到foobar。
总结下:
调用start()后,线程会被放到等待队列,等待CPU调度,并不一定要马上开始,这是将这个线程置于可行动状态。然后通过JVM,线程Thread会调用run()方法,执行本线程的线程体。
错误
解析:
Java虚拟机,对于方法的调用采用的是栈帧(方法调用和方法执行),调用则入栈,完成之后则出栈,是程序自动出栈释放,而不是GC释放
总结下:
JVM内存可简单分为三区:
垃圾回收器(GC)主要针对堆区。
7-28
class Value{
public int i=15;
}
public class Test{
public static void main(String argv[]){
Test t=new Test( );
t.first( );
}
public void first( ){
int i=5;
Value v=new Value( );
v.i=25;
second(v,i);
System.out.println(v.i);
}
public void second(Value v,int i){
i = 0;
v.i = 20;
Value val = new Value( );
v = val;
System.out.println(v.i+" "+i);
}
}
解析:
引用会改变所指的地址的值,所以second中当v.i=20的时候,就把原来first中的v的i值改为了20.接下来又把v指向了新建的一个对象,所以在second中的v现在指的是新的对象val,i值为15
当执行完second后,在first中再次输出v.i的时候,应为前面second中已经把该位置的i的值改为了20,所以输出是20
7-29
Java一个源程序只能有一个public类存在,且类名与文件名相同。Java程序是从main方法开始执行的,public为类加载器提供入口,然后找到public类中的main方法开始执行。如果存在多个public类,程序将不知道该从哪里执行
注意:内部类可以是public的,因为内部类是作为外部类的成员存在的。
解析:
既然是实现接口,就要实现接口的所有方法,相当于重写方法,方法的重写需要满足:两同两小一大:方法名,参数列表相同;返回类型,抛出异常相同或小;访问权限相同或大。
解析:
wait()必须要进行异常捕获
调用wait()或者notify()方法必须采用当前锁调用,即必须采用synchronized中 的对象
class A {
private String a = “aa”;
public boolean methodB() {
String b = “bb”;
final String c = “cc”;
}
}
解析:
a是类中的成员变量,存放在堆区
b,c都是方法中的局部变量,存放在栈区。
解析:当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法内定义的变量将会逐个放入这块栈内存里,随着方法的执行结束,这个方法的内存栈也将自然而然销毁。因此,所有在方法中定义的局部变量都是放在栈内存中的;
在程序中创建一个对象时,这个对象将被保存到运行时数据区中,以便反复利用(因为对象的创建成本通常较大),这个运行时数据区就是堆内存。堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用(在方法的参数传递时很常见),则这个对象依然不会被销毁。只有当一个对象没有任何引用变量引用它时,系统的垃圾回收器才会在合适的时候回收它。
解析:
A:为了更好地组织类,Java提供了包机制。包是类的容器,用于分隔类名空间,所有的示例都属于一个默认的无名包。Java中的包一般均包含相关的类,Java是跨平台的,所以Java中的包和操作系统没有任何关系,Java的包是用来组织文件的一种虚拟文件系统。
B:import语句并没有将对应的Java源文件拷贝到此处,仅仅是引入,告诉编译器有使用外部文件,编译的时候要去读取这个外部文件
C:Java提供的包机制与IDE没有关系
定义在用一个包(package)内的类可以不经过import而直接相互使用。
解析:喂!SHE!
喂——vector
S——stack
H——hashtable
E——Enumeration
解析:
A:Java一律采用Unicode编码方式,每个字符无论中英文字符都占两个字节
B:不同的编码之间是可以转换的,如下:
将字符串S以其自身编码方式分解为字节数组,再将字节数组以你想要输出的编码方式重新编码为字符串。
String newUTF8Str = new String(oldGBKStr.getBytes("GBK"),"UTF-8");
C:正确,Java虚拟机中通常使用UTF-16的方式保存一个字符
D:正确,ResourceBundler能够依据Local的不同,选择性的读取与Local对应后缀的properties文件,以达到国际化目的。
一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
2)禁止进行指令重排序。
volatile只提供了保证访问该变量时,每次都是从内存中读取最新值,并不会使用寄存器缓存该值——每次都会从内存中读取。
而对该变量的修改,volatile并不提供原子性的保证。
由于及时更新,很可能导致另一线程访问最新变量值,无法跳出循环的情况
多线程下计数器必须使用锁保护。
7-30
A:垃圾回收程序是一般在堆上分配空间不够的时候会自己进行一次GC,程序员不需要也不能主动释放内存
B:Java的内存释放由垃圾回收程序进行管理,程序员不能直接进行释放
D:程序员可以调用System.gc()运行垃圾回收器,但不能指定时间。
public class Test
{
public static Test t1 = new Test();
{
System.out.println("blockA");
}
static
{
System.out.println("blockB");
}
public static void main(String[] args)
{
Test t2 = new Test();
}
}
解析:
静态成员变量和静态代码块的优先级是相同的,所以,先定义的先执行。
静态块:程序走到静态块的位置就自动执行,且仅执行一次。
构造块:创建类对象(实例化)操作时就加载一次。
解析:
C:Java不完全算是编译型语言,他编译的字节码文件运行时是解释执行的,其次,Java和C++的类也不都完全是静态绑定的,比如C++的虚函数,Java的父类引用子类对象等情况。
D:Java也可以数组溢出,溢出是会抛出异常,也就是ArrayIndexOutOfBoundsException。
解析:
非纯数字的字符串转换为Integer对象回报数字格式异常
1.基本类型和基本类型封装型进行“==”运算符比较,基本类型封装型会自动拆箱变成基本类型后再进行比较,因此Integer(0)会自动拆箱为int类型再进行比较,显然返回true
int a = 220;
Integer b = 220;
System.out.println(a==b);//true
2.两个Integer类型进行“==”比较,如果其值在-128至127,那么返回true,否则返回false,这跟Integer.valueOf()的缓冲对象有关
Integer c=3;
Integer h=3;
Integer e=321;
Integer f=321;
System.out.println(c==h);//true
System.out.println(e==f);//false
3.两个基本类型的封装型进行equals()比较,首先equals()会比较类型,如果类型相同,则继续比较值,如果值也相同,返回true
Integer a=1;
Integer b=2;
Integer c=3;
System.out.println(c.equals(a+b));//true
4.基本型封装类型调用equals(),但是参数是基本类型,这时候,先会进行自动装箱,基本型转化为其封装类型,再进行3中的比较
int i=1;
int j = 2;
Integer c=3;
System.out.println(c.equals(i+j));//true
7-31
解析:
Java源程序的后缀名为“.java”
经过编译后生成的字节码文件后缀名为“.class”
public class Test
{
public int x;
public static void main(String []args)
{
System. out. println("Value is" + x);
}
}
非静态成员只能被类的实例化对象引用,因此这里在静态方法中访问x会造成编译错误。
当类加载时,static静态方法随着类加载而初始化,此时实例对象还未被创建,但是非静态非静态成员变量需要等到实例对象创建才会被初始化,故无法被引用
解析:
A:for循环的话,很灵活,但是代码不够简介
B:System。arraycopy()源码。可以看到是native方法:native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如C/C++)实现的文件中,
C:本质上是调用arraycopy方法,那么其效率必然是比不上arraycopy的
D:clone的话,返回的是Object【】,需要强制转化
解析:
在一个子类被创建的时候, 首先会在内存中创建一个父类对象,然后在父类对象外部放上子类独有的属性,两者合起来形成一个子类的对象。
所以所谓的继承使子类拥有父类所有的属性和方法其实可以这样理解:子类对象确实拥有父类对象中所有的属性和方法,但是父类对象中的私有属性和方法,子类是无法访问到的,只是拥有,但不能使用。所以子类对象是绝对大于父类对象的,所谓的子类对象只能继承父类非私有的属性及方法的说法是错误的,可以继承,只是无法访问到而已。
import EnclosingOne.InsideOne
1.class Enclosingone
2.{
3. public class InsideOne {}
4.
5.}
6.public class inertest
7.{
8. public static void main(string[]args)
9. {
10. EnclosingOne eo = new EnclosingOne();
11. //insert code here
12. }
13.
14.}
Which statement at line 11 constructs an instance of the inner class?
解析:
见注释:
public class Enclosingone {
//非静态内部类
public class InsideOne {}
//静态内部类
public static class InsideTwo{}
}
class Mytest02{
public static void main(String args []){
Enclosingone.InsideOne obj1 = new Enclosingone().new InsideOne();//非静态内部类对象
Enclosingone.InsideTwo obj2 = new Enclosingone.InsideTwo();//静态内部类对象
}
}