[关闭]
@yexiaoqi 2018-12-03T20:14:19.000000Z 字数 2993 阅读 867

Java相关

java面试总结


Java基础

异常

异常处理的三种机制

  1. 直接抛出给调用者
  2. 自己处理异常
  3. 自己处理异常后在抛出

Java中有虚函数吗?

java类中普通成员函数就是虚函数。网上有一个对比

C++ Java
虚函数 普通函数
纯虚函数 抽象函数
抽象类 抽象类
虚基类 接口

Java双亲委托机制

双亲委派模式的工作原理的是;如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式。

当一个Hello.class这样的文件要被加载时。不考虑我们自定义类加载器,首先会在AppClassLoader检查是否加载过,如果有那就无需再加载了。如果没有,那么会拿到父加载器,然后调用父加载器的loadClass方法。父类中同理会先检查自己是否已经加载过,如果没有再往上。注意这个过程,知道到达Bootstrap classLoader之前,都是没有哪个加载器自己选择加载的。如果父加载器无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException

为什么要设计这种机制

采用双亲委派模式的是好处是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。其次是考虑到安全因素,java核心api中定义类型不会被随意替换,假设通过网络传递一个名为java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,这样便可以防止核心API库被随意篡改。

集合

Map

java中Map遍历的四种方式

方法一:在for循环中使用entry实现Map的遍历

  1. //最常见也是大多数情况下用的最多的,一般在键值对都需要使用
  2. Map <String,String>map = new HashMap<String,String>();
  3. map.put("熊大", "棕色");
  4. map.put("熊二", "黄色");
  5. for(Map.Entry<String, String> entry : map.entrySet()){
  6. String mapKey = entry.getKey();
  7. String mapValue = entry.getValue();
  8. System.out.println(mapKey+":"+mapValue);
  9. }

方法二:在for循环中遍历key或者values,一般适用于只需要map中的key或者value时使用,性能上比entrySet较好;

  1. Map <String,String>map = new HashMap<String,String>();
  2. map.put("熊大", "棕色");
  3. map.put("熊二", "黄色");
  4. //key
  5. for(String key : map.keySet()){
  6. System.out.println(key);
  7. }
  8. //value
  9. for(String value : map.values()){
  10. System.out.println(value);
  11. }

方法三:通过Iterator遍历

  1. Iterator<Entry<String, String>> entries = map.entrySet().iterator();
  2. while(entries.hasNext()){
  3. Entry<String, String> entry = entries.next();
  4. String key = entry.getKey();
  5. String value = entry.getValue();
  6. System.out.println(key+":"+value);
  7. }

方法四:通过键找值遍历,这种方式的效率比较低

  1. for(String key : map.keySet()){
  2. String value = map.get(key);
  3. System.out.println(key+":"+value);
  4. }

JVM

Java类加载过程

类从类加载到JVM中开始,到卸载为止,整个生命周期包括:加载、验证、准备、解析、初始化、使用、卸载七个阶段。

其中类加载过程包括加载、验证、准备、解析、初始化。5个阶段

  1. 加载:由类加载器负责根据一个类的全限定名来读取此类的二进制字节流到JVM内部,并存储在运行时内存区的方法区,然后将其转换为一个与目标类型对应的java.lang.Class对象实例。
  2. 验证:验证类数据信息是否符合JVM规范,是否是一个有效的字节码文件。
  3. 准备:准备阶段是正式为类变量分配内存并设置类变量的初始值阶段,即在方法区中分配这些变量所使用的内存空间。
  4. 解析:是指JVM虚拟机将常量池中的符号引用替换为直接引用的过程。
  5. 初始化:初始化阶段是类加载最后一个阶段,前面的阶段除了在加载阶段可以自定义类加载器以外,其它操作都由JVM主导。到了初始阶段,才开始真正执行类中定义的Java程序代码,比如类构造方法,静态块等。

jvm的三种类加载器

JVM有三种类加载器:BootstrapLoader负责加载系统类,ExtClassLoader负责加载扩展类,AppClassLoader负责加载应用类。他们主要是分工不一样,各自负责不同的区域,另外也是为了实现委托模型。什么是委托模型呢,其实就是当类加载器有加载需求的时候,先请示他的父类使用父类的搜索路径来加入,如果没有找到的话,才使用自己的搜索路径来来搜索类。

下面的图形可以表示三者之间的关系:
BootstrapLoader <---(Extends)----AppClassLoader <---(Extends)----ExtClassLoader

JVM内存模型

JVM内存模型主要由方法区程序计数器虚拟机栈本地方法栈组成。其中,堆和方法区是所有线程共有的,而虚拟机栈,本地方法栈和程序计数器则是线程私有的。

常见笔试题

  1. /**
  2. * 编写程序,输入一个字符,判断它是否为小写字符,如果是,将它转换为大写字母,否则不转换
  3. */
  4. public class homework {
  5. public static void main(String[] args) {
  6. //小写字母的的ascii值为 97-122
  7. //大写字母的ascii值为 65-90
  8. System.out.print("请输入一个字母:");
  9. Scanner input = new Scanner(System.in);
  10. char c = input.next().charAt(0);
  11. if (c>=97 && c<=122){ //判断是不是小写字母
  12. System.out.println("该字母是小写字母");
  13. c = (char)(c-32); //如果是小写字母,将其转换为大写字母
  14. System.out.println("转换之后的大写字母是:"+c);
  15. }else{
  16. System.out.println("该子母不是小写字母!");
  17. }
  18. }
  19. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注