java面试题(三)

简介: 1、Java 中会存在内存泄漏吗,请简单描述。 答: 理论上 Java 因为有垃圾回收机制(GC)不会存在内存泄露问题(这也是 Java 被 广泛使用于服务器端编程的一个重要原因);然而在实际开发中,可能会存在无 用但可达的对象,这些对象不能被 GC 回收,因此也会导致内存泄露的发生。例如 Hibernate 的 Session(一级缓存)中的对象属于持久态,垃圾回收器是不会回收 这些对象的,然而这些对象中可能存在无用的垃圾对象,如果不及时关闭(close) 或清空(flush)一级缓存就可能导致内存泄露。下面例子中的代码也会导致内存泄露。import java.util.A

1、Java 中会存在内存泄漏吗,请简单描述。

答:

理论上 Java 因为有垃圾回收机制(GC)不会存在内存泄露问题(这也是 Java 被

广泛使用于服务器端编程的一个重要原因);然而在实际开发中,可能会存在无

用但可达的对象,这些对象不能被 GC 回收,因此也会导致内存泄露的发生。例如

Hibernate 的 Session(一级缓存)中的对象属于持久态,垃圾回收器是不会回收

这些对象的,然而这些对象中可能存在无用的垃圾对象,如果不及时关闭(close)

或清空(flush)一级缓存就可能导致内存泄露。

下面例子中的代码也会导致内存泄露。

import java.util.Arrays;

import java.util.EmptyStackException;

public class MyStack<T> {

   private T[] elements;

   private int size = 0;

   private static final int INIT_CAPACITY = 16;

   public MyStack() {

       elements = (T[]) new Object[INIT_CAPACITY];

   }

   public void push(T elem) {

       ensureCapacity();

       elements[size++] = elem;

   }

   public T pop() {

       if(size == 0)

           throw new EmptyStackException();

       return elements[--size];

   }

   private void ensureCapacity() {

       if(elements.length == size) {

           elements = Arrays.copyOf(elements, 2 * size + 1);

       }

   }

}


上面的代码实现了一个栈(先进后出(FILO))结构,乍看之下似乎没有什么明

显的问题,它甚至可以通过你编写的各种单元测试。然而其中的 pop 方法却存在

内存泄露的问题,当我们用 pop 方法弹出栈中的对象时,该对象不会被当作垃圾

回收,即使使用栈的程序不再引用这些对象,因为栈内部维护着对这些对象的过

期引用(obsolete reference)。在支持垃圾回收的语言中,内存泄露是很隐蔽的,

这种内存泄露其实就是无意识的对象保持。如果一个对象引用被无意识的保留起

来了,那么垃圾回收器不会处理这个对象,也不会处理该对象引用的其他对象,

即使这样的对象只有少数几个,也可能会导致很多的对象被排除在垃圾回收之外,

从而对性能造成重大影响,极端情况下会引发 Disk Paging(物理内存与硬盘的虚

拟内存交换数据),甚至造成 OutOfMemoryError。

2、抽象的(abstract)方法是否可同时是静态(static), 是否可同时是本地方法(native),是否可同时被synchronized 修饰?

答:

都不能。抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛

盾的。本地方法是由本地代码(如 C 代码)实现的方法,而抽象方法是没有实现

的,也是矛盾的。synchronized 和方法的实现细节有关,抽象方法不涉及实现细

节,因此也是相互矛盾的。

3、阐述静态变量和实例变量的区别。

静态变量是被 static 修饰符修饰的变量,也称为类变量,它属于类,不属于类的

任何一个对象,一个类不管创建多少个对象,静态变量在内存中有且仅有一个拷

贝;实例变量必须依存于某一实例,需要先创建对象然后通过对象才能访问到它。

静态变量可以实现让多个对象共享内存。

4、是否可以从一个静态(static)方法内部发出对非静态 (non-static)方法的调用?

答:

不可以,静态方法只能访问静态成员,因为非静态方法的调用要先创建对象,在

调用静态方法时可能对象并没有被初始化。

5、如何实现对象克隆?

答:

有两种方式:

1). 实现 Cloneable 接口并重写 Object 类中的 clone()方法;

2). 实现 Serializable 接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆,代码如下。

import java.io.ByteArrayInputStream;

import java.io.ByteArrayOutputStream;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.io.Serializable;

public class MyUtil {

   private MyUtil() {

       throw new AssertionError();

   }

   @SuppressWarnings("unchecked")

   public static <T extends Serializable> T clone(T obj) throws

           Exception {

       ByteArrayOutputStream bout = new ByteArrayOutputStream();

       ObjectOutputStream oos = new ObjectOutputStream(bout);

       oos.writeObject(obj);

       ByteArrayInputStream bin = new

               ByteArrayInputStream(bout.toByteArray());

       ObjectInputStream ois = new ObjectInputStream(bin);

       return (T) ois.readObject();

// 说明:调用 ByteArrayInputStream 或 ByteArrayOutputStream

       对象的 close 方法没有任何意义

// 这两个基于内存的流只要垃圾回收器清理对象就能够释放资源,这

       一点不同于对外部资源(如文件流)的释放

   }

}

测试方法

import java.io.Serializable;

/**

* 人类

* @author 骆昊

*

*/

class Person implements Serializable {

   private static final long serialVersionUID = -9102017020286042305L;

   private String name; // 姓名

   private int age; // 年龄

   private Car car; // 座驾

   public Person(String name, int age, Car car) {

       this.name = name;

       this.age = age;

       this.car = car;

   }

   public String getName() {

       return name;

   }

   public void setName(String name) {

       this.name = name;

   }

   public int getAge() {

       return age;

   }

   public void setAge(int age) {

       this.age = age;

   }

   public Car getCar() {

       return car;

   }

   public void setCar(Car car) {

       this.car = car;

   }

   @Override

   public String toString() {

       return "Person [name=" + name + ", age=" + age + ", car=" +

               car + "]";

   }

}

/**

* 小汽车类

* @author 骆昊

*

*/

class Car implements Serializable {

   private static final long serialVersionUID = -5713945027627603702L;

   private String brand; // 品牌

   private int maxSpeed; // 最高时速

   public Car(String brand, int maxSpeed) {

       this.brand = brand;

       this.maxSpeed = maxSpeed;

   }

   public String getBrand() {

       return brand;

   }

   public void setBrand(String brand) {

       this.brand = brand;

   }

   public int getMaxSpeed() {

       return maxSpeed;

   }

   public void setMaxSpeed(int maxSpeed) {

       this.maxSpeed = maxSpeed;

   }

   @Override

   public String toString() {

       return "Car [brand=" + brand + ", maxSpeed=" + maxSpeed +

               "]";

   }

}

class CloneTest {

   public static void main(String[] args) {

       try {

           Person p1 = new Person("Hao LUO", 33, new Car("Benz",

                   300));

           Person p2 = MyUtil.clone(p1); // 深度克隆

           p2.getCar().setBrand("BYD");

// 修改克隆的 Person 对象 p2 关联的汽车对象的品牌属性

// 原来的 Person 对象 p1 关联的汽车不会受到任何影响

// 因为在克隆 Person 对象时其关联的汽车对象也被克隆了

           System.out.println(p1);

       } catch (Exception e) {

           e.printStackTrace();

       }

   }

}

注意:基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛

型限定,可以检查出要克隆的对象是否支持序列化,这项检查是编译器完成的,

不是在运行时抛出异常,这种是方案明显优于使用 Object 类的 clone 方法克隆对

象。让问题在编译的时候暴露出来总是好过把问题留到运行时。

6、GC 是什么?为什么要有 GC?

答:

GC 是垃圾收集的意思,内存处理是编程人员容易出现问题的地方,忘记或者错误

的内存回收会导致程序或系统的不稳定甚至崩溃,Java 提供的 GC 功能可以自动

监测对象是否超过作用域从而达到自动回收内存的目的,Java 语言没有提供释放

已分配内存的显示操作方法。Java 程序员不用担心内存管理,因为垃圾收集器会

自动进行管理。要请求垃圾收集,可以调用下面的方法之一:System.gc() 或

Runtime.getRuntime().gc() ,但 JVM 可以屏蔽掉显示的垃圾回收调用。

垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。垃圾回收器通

常是作为一个单独的低优先级的线程运行,不可预知的情况下对内存堆中已经死

亡的或者长时间没有使用的对象进行清除和回收,程序员不能实时的调用垃圾回

收器对某个对象或所有对象进行垃圾回收。在 Java 诞生初期,垃圾回收是 Java

最大的亮点之一,因为服务器端的编程需要有效的防止内存泄露问题,然而时过

境迁,如今 Java 的垃圾回收机制已经成为被诟病的东西。移动智能终端用户通常

觉得 iOS 的系统比 Android 系统有更好的用户体验,其中一个深层次的原因就在

于 Android 系统中垃圾回收的不可预知性。

补充:垃圾回收机制有很多种,包括:分代复制垃圾回收、标记垃圾回收、增量

垃圾回收等方式。标准的 Java 进程既有栈又有堆。栈保存了原始型局部变量,堆

保存了要创建的对象。Java 平台对堆内存回收和再利用的基本算法被称为标记和

清除,但是 Java 对其进行了改进,采用“分代式垃圾收集”。这种方法会跟 Java

对象的生命周期将堆内存划分为不同的区域,在垃圾收集过程中,可能会将对象

移动到不同区域:

伊甸园(Eden):这是对象最初诞生的区域,并且对大多数对象来说,

这里是它们唯一存在过的区域。

幸存者乐园(Survivor):从伊甸园幸存下来的对象会被挪到这里。

终身颐养园(Tenured):这是足够老的幸存对象的归宿。年轻代收集

(Minor-GC)过程是不会触及这个地方的。当年轻代收集不能把对象放进终身

颐养园时,就会触发一次完全收集(Major-GC),这里可能还会牵扯到压缩,

以便为大对象腾出足够的空间。

与垃圾回收相关的 JVM 参数:

-Xms / -Xmx — 堆的初始大小 / 堆的最大大小  

-Xmn — 堆中年轻代的大小  

-XX:-DisableExplicitGC — 让 System.gc()不产生任何作用

-XX:+PrintGCDetails — 打印 GC 的细节第 244 页 共 485 页

-XX:+PrintGCDateStamps — 打印 GC 操作的时间戳

-XX:NewSize / XX:MaxNewSize — 设置新生代大小/新生代最大大小

-XX:NewRatio — 可以设置老生代和新生代的比例

-XX:PrintTenuringDistribution — 设置每次新生代 GC 后输出幸存者

乐园中对象年龄的分布

-XX:InitialTenuringThreshold / -XX:MaxTenuringThreshold:设置老

年代阀值的初始值和最大值

-XX:TargetSurvivorRatio:设置幸存区的目标使用率

7、Anonymous Inner Class(匿名内部类)是否可以继承它 类?是否可以实现接口?

答:

可以继承其他类或实现其他接口,在 Swing 编程和 Android 开发中常用此方式来

实现事件监听和回调。

8、内部类可以引用它的包含类(外部类)的成员吗?有没有什么限制?

答:

一个内部类对象可以访问创建它的外部类对象的成员,包括私有成员。

9、Java 中的 final 关键字有哪些用法?

(1)修饰类:表示该类不能被继承;(2)修饰方法:表示方法不能被重写;(3)修饰变

量:表示变量只能一次赋值以后值不能被修改(常量)。

10、日期和时间:

如何取得年月日、小时分钟秒?

如何取得从 1970 年 1 月 1 日 0 时 0 分 0 秒到现在的毫秒数?

如何取得某月的最后一天?

如何格式化日期?

答:

问题 1:创建 java.util.Calendar 实例,调用其 get()方法传入不同的参数即可获

得参数所对应的值。Java 8 中可以使用 java.time.LocalDateTimel 来获取,代码

如下所示。

public class DateTimeTest {

   public static void main(String[] args) {

       Calendar cal = Calendar.getInstance();

       System.out.println(cal.get(Calendar.YEAR));

       System.out.println(cal.get(Calendar.MONTH)); // 0 - 11

       System.out.println(cal.get(Calendar.DATE));

       System.out.println(cal.get(Calendar.HOUR_OF_DAY));

       System.out.println(cal.get(Calendar.MINUTE));

       System.out.println(cal.get(Calendar.SECOND));

// Java 8

       LocalDateTime dt = LocalDateTime.now();

       System.out.println(dt.getYear());

       System.out.println(dt.getMonthValue()); // 1 - 12

       System.out.println(dt.getDayOfMonth());

       System.out.println(dt.getHour());

       System.out.println(dt.getMinute());

       System.out.println(dt.getSecond());

   }

}

Calendar.getInstance().getTimeInMillis();

System.currentTimeMillis();

Clock.systemDefaultZone().millis(); // Java 8

Calendar time = Calendar.getInstance();

time.getActualMaximum(Calendar.DAY_OF_MONTH);

import java.text.SimpleDateFormat;

import java.time.LocalDate;

import java.time.format.DateTimeFormatter;

import java.util.Date;

class DateFormatTest {

   public static void main(String[] args) {

       SimpleDateFormat oldFormatter = new

               SimpleDateFormat("yyyy/MM/dd");

       Date date1 = new Date();

       System.out.println(oldFormatter.format(date1));

// Java 8

       DateTimeFormatter newFormatter =

               DateTimeFormatter.ofPattern("yyyy/MM/dd");

       LocalDate date2 = LocalDate.now();

       System.out.println(date2.format(newFormatter));

   }

}

相关文章
|
8天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
28 2
|
13天前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
18天前
|
存储 缓存 Oracle
Java I/O流面试之道
NIO的出现在于提高IO的速度,它相比传统的输入/输出流速度更快。NIO通过管道Channel和缓冲器Buffer来处理数据,可以把管道当成一个矿藏,缓冲器就是矿藏里的卡车。程序通过管道里的缓冲器进行数据交互,而不直接处理数据。程序要么从缓冲器获取数据,要么输入数据到缓冲器。
Java I/O流面试之道
|
14天前
|
存储 缓存 Java
大厂面试必看!Java基本数据类型和包装类的那些坑
本文介绍了Java中的基本数据类型和包装类,包括整数类型、浮点数类型、字符类型和布尔类型。详细讲解了每种类型的特性和应用场景,并探讨了包装类的引入原因、装箱与拆箱机制以及缓存机制。最后总结了面试中常见的相关考点,帮助读者更好地理解和应对面试中的问题。
39 4
|
15天前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
55 4
|
1月前
|
存储 安全 算法
Java面试题之Java集合面试题 50道(带答案)
这篇文章提供了50道Java集合框架的面试题及其答案,涵盖了集合的基础知识、底层数据结构、不同集合类的特点和用法,以及一些高级主题如并发集合的使用。
91 1
Java面试题之Java集合面试题 50道(带答案)
|
27天前
|
存储 Java 程序员
Java面试加分点!一文读懂HashMap底层实现与扩容机制
本文详细解析了Java中经典的HashMap数据结构,包括其底层实现、扩容机制、put和查找过程、哈希函数以及JDK 1.7与1.8的差异。通过数组、链表和红黑树的组合,HashMap实现了高效的键值对存储与检索。文章还介绍了HashMap在不同版本中的优化,帮助读者更好地理解和应用这一重要工具。
54 5
|
26天前
|
存储 Java
[Java]面试官:你对异常处理了解多少,例如,finally中可以有return吗?
本文介绍了Java中`try...catch...finally`语句的使用细节及返回值问题,并探讨了JDK1.7引入的`try...with...resources`新特性,强调了异常处理机制及资源自动关闭的优势。
20 1
|
1月前
|
Java 程序员
Java 面试高频考点:static 和 final 深度剖析
本文介绍了 Java 中的 `static` 和 `final` 关键字。`static` 修饰的属性和方法属于类而非对象,所有实例共享;`final` 用于变量、方法和类,确保其不可修改或继承。两者结合可用于定义常量。文章通过具体示例详细解析了它们的用法和应用场景。
28 3
|
2月前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
421 37
下一篇
无影云桌面