zoukankan      html  css  js  c++  java
  • 面试之JavaSE高级

    1.说说你对 Java 中反射的理解

    反射(Reflection)机制:其实就是程序在运行的时候可以获取任意一个类的内部信息,也可以说是动态获取吧。

    Java 中 的 反 射 首 先 是 能 够 获 取 到 Java 中 要 反 射 类 的 字 节 码 , 获 取 字 节 码 有 三 种 方 法 ,
    1.Class.forName(className) 2.类名.class 3.this.getClass()。

    然后将字节码中的方法,变量,构造函数等映射成相应的 Method、Filed、Constructor 等类,这些类提供了丰富的方法可以被我们所使用。

    2.动静态代理的区别,什么场景使用?

    静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。
    静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。
    动态代理是实现 JDK 里的 InvocationHandler 接口的 invoke 方法,但注意的是代理的是接口,也就是你的业务类必须要实现接口,通过 Proxy 里的 newProxyInstance 得到代理对象。
    还有一种动态代理 CGLIB,代理的是类,不需要业务类继承接口,通过派生的子类来实现代理。通过在运行时,动态修改字节码达到修改类的目的。
    AOP 编程就是基于动态代理实现的,比如著名的 Spring 框架、Hibernate 框架等等都是动态代理的使用例子。

    3.你所知道的设计模式有哪些(只写常用的)

    创建型模式:工厂方法模式、抽象工厂模式、单例模式、建造者模式

    结构型模式:适配器模式, 代理模式, 享元模式

    行为型模式:策略模式,  观察者模式.

    4.谈谈 JVM 的内存结构和内存分配

    Java 虚拟机将其管辖的内存大致分三个逻辑部分:方法区(Method Area)、Java 栈和 Java 堆

    堆区:
    1.存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)
    2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身.
    3.一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。

    栈区:
    1.每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中
    2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
    3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
    4.由编译器自动分配释放 ,存放函数的参数值,局部变量的值等.

    静态区/方法区:
    1.方法区又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
    2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
    3.全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。

    5. Java 中引用类型都有哪些?(重要)

    Java 中对象的引用分为四种级别,这四种级别由高到低依次为:强引用、软引用、弱引用和虚引用。

    强引用(StrongReference)
    这个就不多说,我们写代码天天在用的就是强引用。如果一个对象被被人拥有强引用,那么垃圾回收器绝不会回收它。
    当内存空间不足,Java 虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会靠随意
    回收具有强引用的对象来解决内存不足问题。

    软引用(SoftReference)
    如果一个对象只具有软引用,那么如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会
    回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。

    弱引用(WeakReference)
    如果一个对象只具有弱引用,那该类就是可有可无的对象,因为只要该对象被 gc 扫描到了随时都会把它干
    掉。弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。

    虚引用(PhantomReference)
    "虚引用"顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对
    象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。虚引用主要用来跟踪对象被垃
    圾回收的活动。

     

    6. 解释内存中的栈 (stack) 、堆 (heap) 和方法区 (method area) 的用法

    通常我们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的现场保存都使用 JVM 中的栈空间;
    而通过 new 关键字和构造器创建的对象则放在堆空间,堆是垃圾收集器管理的主要区域

    方法区和堆都是各个线程共享的内存区域,用于存储已经被 JVM 加载的类信息、常量、静态变量、JIT 编译器编译后的代码等数据;

    程序中的字面量(literal)如直接书写的 100、"hello"和常量都是放在常量池中,常量池是方法区的一部分。

    栈空间操作起来最快但是栈很小,通常大量的对象都是放在堆空间,
    栈和堆的大小都可以通过 JVM 的启动参数来进行调整,栈空间用光了会引发 StackOverflowError,
    而堆和常量池空间不足则会引发 OutOfMemoryError。

    String str = new String("hello");
    上面的语句中变量 str 放在栈上,用 new 创建出来的字符串对象放在堆上,而"hello"这个字面量是放在方法区的。

    7.描述一下 JVM 加载 class 

    虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的 Java 类型,这就是虚拟机的类加载机制

    类的加载是指把类的.class文件中的数据读入到内存中,通常是创建一个字节数组读入.class 文件,然后产生与所加载类对应的 Class 对象。
    加载完成后,Class 对象还不完整,所以此时的类还不可用。当类被加载后就进入连接阶段,这一阶段包括验证、准备
    (为静态变量分配内存并设置默认的初始值)和解析(将符号引用替换为直接引用)三个步骤。最后 JVM 对类进行
    初始化,包括:如果类存在直接的父类并且这个类还没有被初始化,那么就先初始化父类;如果类中存在初始化语句,就依次执行这些初始化语句。

    8.Java 中为什么会有 GC 机制呢?

    Java 中为什么会有 GC 机制呢?
    • 安全性考虑;-- for security.
    • 减少内存泄露;-- erase memory leak in some degree.
    • 减少程序员工作量。-- Programmers don't worry about memory releasing.

    9.对于 Java 的 GC 哪些内存需要回收

    内存运行时 JVM 会有一个运行时数据区来管理内存。

    它主要包括 5 大部分:程序计数器(Program CounterRegister)、虚拟机栈(VM Stack)、本地方法栈(Native Method Stack)、方法区(Method Area)、堆(Heap).

    而其中程序计数器、虚拟机栈、本地方法栈是每个线程私有的内存空间,随线程而生,随线程而亡。

    但方法区和堆就不同了,一个接口的多个实现类需要的内存可能不一样,我们只有在程序运行期间才会知道会创

    建哪些对象,这部分内存的分配和回收都是动态的,GC 主要关注的是这部分内存。

    10.Java 的 GC 什么时候回收垃圾

    垃圾回收器的运行时间是不确定的,由JVM决定,在运行时是间歇执行的。

    虽然可以通过System.gc()来强制回收垃圾,但是这个命令下达后无法保证JVM会立即响应执行,

    但经验表明,下达命令后,会在短期内执行的请求。JVM通常会感到内存紧缺时候去执行垃圾回收操作。

    11.在开发中遇到过内存溢出么?原因有哪些?解决方法有哪些?

    内存溢出是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于虚拟机能提供的最大内存。

     引起内存溢出的原因有很多种,常见的有以下几种:
      1.内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
      2.集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
      3.代码中存在死循环或循环产生过多重复的对象实体;
      4.使用的第三方软件中的BUG;
      5.启动参数内存值设定的过小;

    内存溢出的解决方案
          第一步,修改JVM启动参数,直接增加内存。(-Xms,-Xmx参数一定不要忘记加。)

      第二步,检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误。

      第三步,对代码进行走查和分析,找出可能发生内存溢出的位置。

    重点排查以下几点:
      1.检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。
      2.检查代码中是否有死循环或递归调用。 

      3.检查是否有大循环重复产生新对象实体。 

      4.检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中   数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。 

      5.检查List、MAP等集合对象是否有使用完后,未清除的问题。List、MAP等集合对象会始终存有对对象的引用,使得这些对象不能被GC回收。

      第四步,使用内存查看工具动态查看内存使用情况

    拓展:http://www.cnblogs.com/yangyi1024/p/6417874.html

  • 相关阅读:
    UML统一建模语言笔记
    从零开始学JavaWeb
    也谈微信小程序
    Memcached,你懂的
    一个简单的配置管理器(SettingManager)
    我的AngularJS 学习之旅
    .NET Core 跨平台
    ASP.NET Core 中间件自定义全局异常处理
    面试必考题——递归解题套路
    程序员着装指南
  • 原文地址:https://www.cnblogs.com/chenshuyong/p/10037689.html
Copyright © 2011-2022 走看看