剑指Java面试 -- Java基础篇

本文深入探讨Java的基础知识,包括平台无关性、JVM的工作原理、反射机制、类加载过程和ClassLoader。重点讲解了JVM如何加载.class文件,类加载器的双亲委派机制,以及Java内存模型,特别是堆和栈的区别。同时,讨论了不同JDK版本中intern()方法的行为差异。
摘要由CSDN通过智能技术生成

仅作为面试知识点记录,具体课程请在慕课网搜索剑指Java面试-Offer直通车

Java知识考点

谈谈你对Java的理解

  • 平台无关性
  • GC
  • 语言特性
  • 面向对象
  • 类库
  • 异常处理

Compile Once, Run Anywhere如何实现

  • 编译时
  • 运行时

在这里插入图片描述

Java源码首先被编译成字节码,再由不同平台的JVM进行解析,Java语言在不同的平台上运行时不需要进行重新编译,Java虚拟机在执行字节码的时候,把字节码转换成具体平台上的机器指令

为什么JVM不直接将源码解析成机器码去执行

  • 准备工作:每次执行都需要各种检查
  • 兼容性:也可以将别的语言解析成字节码

JVM如何加载.class文件

Java虚拟机

在这里插入图片描述

  • Class Loader:依据特定格式,加载class文件到内存
  • Execution Engine:对命令进行解析
  • Native Interface:融合不同开发语言的原生库为Java所用

谈谈反射

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为JAVA语言的反射机制。

写一个反射的例子

Robot.java

package com.interview.javabasic.reflect;

  

public class Robot {

    private String name;

    public void sayHi(String helloSentence){

        System.out.println(helloSentence + " " + name);

    }

    private String throwHello(String tag){

        return "Hello " + tag;

    }

    static {

        System.out.println("Hello Robot");

    }

}

ReflectSample.java

package com.interview.javabasic.reflect;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReflectSample {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException, NoSuchFieldException {
        Class rc = Class.forName("com.interview.javabasic.reflect.Robot");
        Robot r = (Robot) rc.newInstance();
        System.out.println("Class name is " + rc.getName());
        Method getHello = rc.getDeclaredMethod("throwHello", String.class);
        getHello.setAccessible(true);
        Object str = getHello.invoke(r, "Bob");
        System.out.println("getHello result is " + str);
        Method sayHi = rc.getMethod("sayHi", String.class);
        sayHi.invoke(r, "Welcome");
        Field name = rc.getDeclaredField("name");
        name.setAccessible(true);
        name.set(r, "Alice");
        sayHi.invoke(r, "Welcome");
        System.out.println(System.getProperty("java.ext.dirs"));
        System.out.println(System.getProperty("java.class.path"));

    }
}

类从编译到执行的过程

  • 编译器将Robot.java源文件编译为Robot.class字节码文件
  • ClassLoader将字节码转换为JVM中的Class<Robot>对象
  • JVM利用Class<Robot>对象实例化为Robot对象

谈谈ClassLoader

ClassLoader在Java中有着非常重要的作用,它主要工作在Class装载的加载阶段,其主要作用是从系统外部获得Class二进制数据流。它是Java的核心组件,所有的Class都是由ClassLoader进行加载的,ClassLoader负责通过将Class文件里的二进制数据流装载进系统,然后交给Java虚拟机进行连接、初始化等操作。

模拟ClassLoader

MyClassLoader.java

public class MyClassLoader extends ClassLoader {
    private String path;
    private String classLoaderName;

    public MyClassLoader(String path, String classLoaderName) {
        this.path = path;
        this.classLoaderName = classLoaderName;
    }

    //用于寻找类文件
    @Override
    public Class findClass(String name) {
        byte[] b = loadClassData(name);
        return defineClass(name, b, 0, b.length);
    }

    //用于加载类文件
    private byte[] loadClassData(String name) {
        name = path + name + ".class";
        InputStream in = null;
        ByteArrayOutputStream out = null;
        try {
            in = new FileInputStream(new File(name));
            out = new ByteArrayOutputStream();
            int i = 0;
            while ((i = in.read()) != -1) {
                out.write(i);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
                in.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return out.toByteArray();
    }
}

ClassLoaderCheck.java

public class ClassLoaderChecker {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        MyClassLoader m = new MyClassLoader("/Users/baidu/Desktop/", "myClassLoader");
        Class c = m.loadClass("Wali");
        System.out.println(c.getClassLoader());
        
    }
}

谈谈类加载器的双亲委派机制

在这里插入图片描述

为什么要使用双亲委派机制去加载类

  • 避免多份同样字节码的加载

类的加载方式

  • 隐式加载:new
  • 显式加载:loadClass,forName等

loadClass和forName的区别

类的装载过程

在这里插入图片描述

你了解Java的内存模型吗

内存简介

在这里插入图片描述

  • 32位处理器:2^32的可寻址范围
  • 64位处理器:2^64的可寻址范围

地址空间的划分

  • 内核空间
  • 用户空间

JVM内存模型-JDK8

在这里插入图片描述

  • 线程私有:程序计数器、虚拟机栈、本地方法栈
  • 线程共享:MetaSpace、Java堆
程序计数器(Program Counter Register)
  • 当前线程所执行的字节码行号指示器(逻辑)
  • 改变计数器的值来选取下一条需要执行的字节码指令
  • 和线程是一对一关系即“线程私有”
  • 对Java方法计数,如果是Native方法则计数器值位Undefined
  • 不会发生内存泄漏
Java虚拟机栈(Stack)
  • Java方法执行的内存模型
  • 包含多个栈帧

在这里插入图片描述

局部变量表和操作数栈
  • 局部变量表:包含方法执行过程中的所有变量
  • 操作数栈:入栈、出栈、复制、交换、产生消费变量
执行add(1,2)

在这里插入图片描述

递归为什么会引发java.lang.StackOverflowError异常

在这里插入图片描述

  • 递归过深,栈帧数超出虚拟栈深度
本地方法栈
  • 与虚拟机栈相似,主要作用于标注了native的方法

元空间(MetaSpace)与永久代(PermGen)的区别

  • 元空间使用本地内存,而永久代使用的是jvm的内存

MetaSpace相比PermGen的优势

  • 字符串常量池存在永久代中,容易出现性能问题和内存溢出
  • 类和方法的信息大小难以确定,给永久代的大小指定带来困难
  • 永久代会为GC带来不必要的复杂性
  • 方便HotSpot与其他JVM如Jrockit的集成

Java堆(Heap)

  • 对象实例的分配区域
    在这里插入图片描述

  • GC管理的主要区域
    在这里插入图片描述

JVM三大性能调优参数-Xms -Xmx -Xss的含义

  • -Xss:规定了每个线程虚拟机栈(堆栈)的大小
  • -Xms:堆的初始值
  • -Xmx:堆能达到的最大值

Java内存模型中堆和栈的区别——内存分配策略

  • 静态存储:编译时确定每个数据目标在运行时的存储空间需求
  • 栈式存储:数据区需求在编译时未知,运行时模块入口前确定
  • 堆式存储:编译时或运行时模块入口都无法确定,动态分配

Java内存模型中堆和栈的区别

  • 联系:引用对象、数组时,栈里定义变量保存堆中目标的首地址
    在这里插入图片描述

  • 管理方式:栈自动释放,堆需要GC

  • 空间大小:栈比堆小

  • 碎片相关:栈产生的碎片远小于堆

  • 分配方式:栈支持静态和动态分配,而堆仅支持动态分配

  • 效率:栈的效率比堆高

元空间、堆、线程独占部分间的联系——内存角度

在这里插入图片描述

不同JDK版本之间的intern()方法的区别——JDK6 VS JDK6+

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值