文章目录
1、抽象的(abstract)方法是否可同时是静态的(static),是否可同时是本地方法([native](Java关键字之native详解_java_脚本之家 (jb51.net))),是否可同时被synchronized 修饰?
返回目录
都不能。抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的。本地方法是由本地代码(如C代码)实现的方法,而抽象方法是没有实现的,也是矛盾的。synchronized 和方法的实现细节有关,抽象方法不涉及实现细节,因此也是相互矛盾的。
2、阐述静态变量和实例变量的区别。
返回目录
静态变量是被 static 修饰符修饰的变量,也称为类变量,它属于类,不属于类的任何一个对象,一个类不管创建多少个对象,静态变量在内存中有且仅有一个拷贝;实例变量必须依存于某一实例,需要先创建对象然后通过对象才能访问到它。静态变量可以实现多个对象共享内存。
补充:
在 java 开发中,上下文类 和 工具类中通常会有大量的静态成员。
3、是否可以从一个静态(static)方法内部发出对非静态(non-static)方法的调用?
返回目录
不可以,静态方法只能访问静态成员,因为非静态方法的调用要先创建对象,在调用静态方法时可能对象并没有被初始化。
4、如何实现对象克隆?
返回目录
有两种 方式:
-
实现 Cloneable 接口并重写 Object 类中的clone() 方法;
-
实现 Serializable 接口,通过对象的序列化和发序列化实现克隆,可以实现真正的深度克隆,代码如下。
package day220804; import java.io.*; /** * @ProjectName: java_test_01 * @Package: day220804 * @ClassName: MyUtil * @Author: [Lucky Star] * @Date: 2022/8/4 20:00 * @Version: V1.0 **/ // 克隆的工具类 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 方法没有任何意义 // 这两个基于内存的流只要垃圾回收器清理对象就能够释放资源,这一点不同于对外部资源(如文件)的释放。 } }
package day220804; import java.io.Serializable; import java.util.UUID; /** * @ProjectName: java_test_01 * @Package: day220804 * @ClassName: Person * @Author: [Lucky Star] * @Date: 2022/8/4 20:05 * @Version: V1.0 **/ // Person 实体类 public class Person implements Serializable { private static final long serialVersionUID = -5713940265465161L; // 姓名 private String name; // 年龄 private int age; // 座驾 private Car car; public Person() { } public Person(String name, int age, Car car) { this.name = name; this.age = age; this.car = car; } public static long getSerialVersionUID() { return serialVersionUID; } 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 + '}'; } }
package day220804; import java.io.Serializable; import java.util.UUID; /** * @ProjectName: java_test_01 * @Package: day220804 * @ClassName: Car * @Author: [Lucky Star] * @Date: 2022/8/4 20:09 * @Version: V1.0 **/ public class Car implements Serializable { private static final long serialVersionUID = -91321321654654L; // 品牌 private String brand; // 最高时速 private int maxSpeed; public Car() { } public Car(String brand, int maxSpeed){ this.brand = brand; this.maxSpeed = maxSpeed; } public static long getSerialVersionUID() { return serialVersionUID; } 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 + '}'; } }
package day220804; /** * @ProjectName: java_test_01 * @Package: day220804 * @ClassName: CloneTest * @Author: [Lucky Star] * @Date: 2022/8/4 20:12 * @Version: V1.0 **/ public 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); System.out.println(p2); } catch (Exception e) { e.printStackTrace(); } } }
注意:
基于序列化和反序列化实现克隆的不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种方案明显优于还是用 Object类的 clone 方法克隆对象。让问题在编译的时候暴露出来总是好过把问题留到运行时。
5、 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),这里可能还会牵制到压缩,以便为大对象腾出足够的空间。