Java foundation review-2
1.继承执行子父类执行关系?
这里执行顺序其实很容易理解,子类在实例化,会先实例化父类,但是在子类实例化前会先把静态成员也即是类成员(static修饰)先加载,未被static 修饰是叫对象变量那么就是先static父->static子->实例化父->实例化子。
package one;
public class Son extends Father{
public Son(){
System.out.println("son");
}
static {
System.out.println("i am son static block");
}
public static void main(String[] args) {
Son s = new Son();//或者 Father f = new Son();结果一样
}
}
class Father{
public Father(){
System.out.println("Father");
}
static {
System.out.println("i am static block");
}
}
//输出情况
//i am static block
//i am son static block
//Father
//son
2.在类方法中绝对不能调用实例方法吗?
不能这么绝对,通常情况下,类成员是在类加载时候创建,而实例方法在对象创建时才有,如果实例还没创建那么类方法就不可能调用到实例方法,也就说只有创建了实例时类方法才能调用实例方法。
//一个典型例子
package one;
public class Instance {
public static void main(String[] args) {
// test();在为创建实例就调用显然错误,main方法相当于类成员
Instance instance = new Instance();
instance.test();
}
//实例方法
public void test(){
System.out.println("this is a instance methon");
}
}
3.多态访问原理
成员变量:它有两种访问方式
-
通过(类.成员变量)访问,看等号左边是谁优先使用,没有则向上找
-
通过成员方法访问,看该方法属于谁优先使用它,没有向上找
成员方法:看new谁,优先访问它的方法
package one; public class Fu { public int i=1; String fustr="fu"; public void override(){ System.out.println("父类方法"); } public void specialFu(){ System.out.println("父类特有方法"); } } package one; public class Zi extends Fu{ public int i=10; @Override public void override() { System.out.println("子类方法"); } public static void main(String[] args) { Fu fu = new Zi(); Zi zi = new Zi(); System.out.println(fu.i);//1 System.out.println(zi.i);//10 System.out.println(fu.fustr);//fu System.out.println(zi.fustr);//fu fu.override();//子类方法 运行看右边可以知道override在父类有值,可通过编译就可以运行,运行看等号右边,就是使用子类方法。 zi.override();//子类方法 fu.specialFu();//父类特有方法 zi.specialFu();//父类特有方法 fu.specialZi();//错误,父类引用指不到子类特有方法,编译看左边,运行看右边是指编译fu指向父类引用但是编译时无法加载子类特有方法,故编译报错。 zi.specialZi(); } }
4.抽象类和接口关系
抽象类与接口不同:
-
抽象类可以有构造方法,不过不能new,接口不能
-
抽象类可以有普通成员变量,方法,接口不能且它的方法只能是public abstract修饰,变量必须是常量即final static修饰
-
接口多继承,抽象类单继承。因为抽象类可以写普通成员方法,假使一个类继承两个具有相同方法抽象类,那么该类使用谁?
相同点:
-
都可以有静态方法。
5.继承方法中返回值与修饰符关系
修饰符:子类的限定修饰符不能缩小父类的范围,如父修public 子只能为public
返回值:子类返回值必须是等于或者父类返回值的子类,如父返回值为Object子返回值为String
重载:什么都不看,只看方法签名,不一致即重载,无关返回值、权限修饰符。
6.简单总结
- 静态方法不能调用非静态方法
- 成员方法不能声明静态的局部变量
7.a=a+b与a+=b区别
如果a或者b有byte、short、char类型那么a+b会先结果为int类型,结果不会隐式转换,也即是说要强转,而a+=b不需要。两者都会损失精度。
ackage three;
public class Parse {
public static void main(String[] args) {
byte a=0;
short c =1;
// c=a+c;//会报错
c=(short)(a+c);
c+=a;
}
}
8.JVM工作原理是什么?
JVM组成:
-
类装载子系统
类加载过程:
- 装载:更加相应路径查找相应class文件,然后导入
- 链接:
- 检查:检查装载的class文件正确性
- 准备:给类的静态变量分配内存空间
- 解析:将符号引用转换直接引用(可选)
- 初始化:初始化静态变量和静态代码块
组成:
- Bootstrap ClassLoad : JVM根加载器,C++写的 JVM已启动就初始化此ClassLoad, 并完成sun JDK 的实现(jre/lib/rt.jar包中,该包中包含Java规范定义的所有接口和实现)
- Excension ClassLoad : JVM用于加载扩展的jar包
- System ClassLoad : JVM用来加载制定Classpath的jar包及目录,在sun JDK 中它对应类名为AppClassLoad.
- User-Defined ClassLoad :程序员自定义的继承与ClassLoad的抽象类,自定义可加载非Classpath的jar和目录
-
内存空间
-
方法区
方法区用来存储被虚拟机加载的类信息、常量、静态变量和编译器编译后的代码等数据。在类加载 器加载 class文件时,这些信息将会被提取出来,并存储到方法区中。由于这个区域是所有线程共享的区 域,因此,它被设计为线程安全的。 方法区中还存放了运行时的常量池,最典型的应用就是字符串常量,例如,定义了如下语句: String s=”Hello”; String s1=”Hello”;其中,“Hello”是字符串常量,存储在常量池中,两个字符串引用s和s1 都指向常量池中的“Hello”
-
堆
堆是虚拟机启动的时候创建的,被所有线程共享的区域。这块区域主要用来存放对象的实例,通过new操作创建出来的对象的实例都存储在堆空间中,因此,堆就成为垃圾回收器管理的重点区域
-
虚拟机栈
栈是线程私有的区域,每当有新的线程创建时,就会给它分配一个栈空间,当线程结束后,栈空间 就被回收,因此,栈与线程拥有相同的生命周期。栈主要用来实现Java语言中方法的调用与执行,每个 方法在被执行的时候,都会创建一个栈帧用来存储这个方法的局部变量、操作栈、动态链接和方法出口 等信息。当进行方法调用时,通过压栈与弹栈操作进行栈空间的分配与释放。当一个方法被调用的时候, 会压入一个新的栈帧到这个线程的栈中,当方法调用结束后,就会弹出这个栈帧,从而回收调用这个方 法使用的栈空间。
-
程序计数器
程序计数器也是线程私有的资源,JVM会给每个线程创建单独的程序计数器。它可以被看作是当前 线程执行的字节码的行号指示器,解释器的工作原理就是通过改变这个计数器的值来确定下一条需要被 执行的字节码指令,程序控制的流程(循环、分支、异常处理、线程恢复)都是通过这个计数器来完 成的。
-
本地方法栈
本地方法栈与虚拟机栈的作用是相似的,唯一不同的是虚拟机栈为虚拟机执行Java方法(也就是字 节码)服务,而本地方法栈则是为虚拟机使用到的 Native(本地)方法服务。 Native(本地)方法接口 都会使用某种本地方法栈,当线程调用Java方法时,JVM会创建一个新的栈帧并压入虚拟机栈。然而当它调用的是本地方法时,虚拟机栈保持不变,不会在线程的虚拟机栈中压入新的帧,而是简单地动态链接并直接调用指定的本地方法。如果某个虚拟机实现的本地方法接口使用的是C+连接模型,那么,它的本地方法栈就是C++栈。
-
-
执行引擎
执行引擎主要负责执行字节码。方法的字节码是由Java虚拟机的指令序列构成的,每一条指令包含 一个单字节的操作码,后面跟随0个或多个操作数。当执行引擎执行字节码时,首先会取一个操作码, 如果这个操作码有操作数,会接着取得它的操作数。然后执行这个操作,执行完成后会继续取得下一个 操作码执行。
-
垃圾回收器
主要回收程序中不用的内存。
9.容器相关类
Hashtable和HashMap区别:
- HashMap是Hashtable的轻量级实现,都实现了Map接口,主要区别是HashMap允许一个key或value为null,而Hashtable不允许key或value为null
- HashMap线程不安全、Hashtable线程安全,因此HashMap效率更高
- Hashtable采用Enumeration遍历,而HashMap采用Iterator遍历
- 两者采用hash算法几乎一致
10.List<? extends T>和List<? super T>区别
List<? extends T> 表达类型的上界,即类型要为T或者T的子类,下面的表达式都是对的。
List<? extends Number> list = new ArrayList<Integer>();
List<? extends Number> list1 = new ArrayList<Float> ();//Float 和Integer都是Number子类
List<? extends T>在读数据时能够确保读出是T或者其子类,但是不能读Integer因为可能为Float,在写数据时也因为list可能是其它类型导致无法写入(原因:每个List内对象类型应该一致)
读数据:只能保证读Number类型 写数据:不能写
List<? super T>表达类型上界,即类型必须为T或其超类。
List<? super Float> list = new ArrayList<Number>();
List<? super Float> list1 = new ArrayList<Object>();
读数据:无法读 写数据:只能写入Float