【秋招冲刺-每日打卡】应届生JAVA岗-每日5道高频面试题【Day4】-基础篇(4)

简介: 【秋招冲刺-每日打卡】应届生JAVA岗-每日5道高频面试题【Day4】-基础篇(4)

文章大纲

一: 深拷贝和浅拷贝的区别是什么?

(一) 浅拷贝:

(二) 深拷贝:

二: throw和throws的区别?

三: 受检查异常和运行时异常与有何区别?

四: 列举一些工作中你常遇到的运行时异常

五: SimpleDateFormat是线程安全的吗?如果不是,怎么解决它线程不安全的问题?

每日小结

image.png

大家好,这里是IT学习日记,一个非双一流大学毕业的深漂族,年少曾憧憬大厂,面试过许多家公司,也曾踩过无数坑,深知面试技巧和知识广度与深度对一个应届生乃至工作多年的开发者的重要性。

故特意收集了各个公司、大厂的面试高频题,通过每天打卡的方式,和大家一起记录和学习,希望能够帮助到应届生和开发者们少走弯路,一起冲向大厂!!!

image.png

一: 深拷贝和浅拷贝的区别是什么?


 在讲解拷贝知识前,我们先来了解下JAVA中的数据类型,主要分为以下两种:


 1、基本类型: 也叫做值类型,主要是值JAVA自带的8种数据类型即:byte、char、short、int、long、float、double、boolean。


 2、引用类型: JAVA中除了基本类型,其他的称为引用类型,常见的如:对象、数组、枚举 等。


 3、在JAVA中,基本类型是存放在栈中的,而引用类型实际上是存在堆中,然后在栈中存在一个指针指向堆中的实际对象数据。

image.png

拷贝: 实际上就是复制的意思,跟平常我们使用ctrl+c命令的效果一样,但在JAVA中,它是区分为浅拷贝和深拷贝两种。


(一) 浅拷贝:

 复制出来的对象跟原来的对象有相同的值,但是如果被复制对象中包含有其他类型对象时,只会复制这个对象的引用


 如:A对象中有一个属性叫demo是B类型的对象,那么浅拷贝时,复制出来的C对象里面的demo属性和A对象都是指向同一个对象,如果修改C对象里面的demo属性,那么A对象中的demo属性也会被修改,因为它们指向的是相同的一个对象。


 对象想具有拷贝功能,需要满足以下的两个条件(注意:Object提供的clone方法默认只实现浅拷贝):


 1、实现Clonable接口


 2、重写Clonable接口中的clone方法


 浅拷贝样例图和代码:

image.png

image.png

public class CloneDemo {
    public static void main(String[] args) throws Exception{
        Demo1 demo1 = new Demo1("1",2,new Demo2("3",4));
        System.out.println("原来的对象数据:" + demo1);
        // 浅拷贝
        Demo1 cloneDemo = (Demo1) demo1.clone();
        System.out.println("拷贝出来的对象数据:" + cloneDemo);
        // 修改拷贝对象的属性
        cloneDemo.setAge(100);
        cloneDemo.setAgeCone(100);
        cloneDemo.setUserName("test");
        cloneDemo.setS1(Short.valueOf("200"));
        cloneDemo.getDemo2().setDemo2Age(1000);
        System.out.println();
        System.out.println("修改拷贝出来的对象引用类型属性:被拷贝对象的数据" + demo1);
        System.out.println("修改拷贝出来的对象引用类型属性:拷贝出来的对象数据" + cloneDemo);
    }
}
@Data
class Demo1 implements Cloneable{
    private String userName;
    private Integer age;
    private Short s1 = new Short("100");
    private Integer ageCone = new Integer(900);
    private Demo2 demo2;
    public Demo1(String userName, Integer age, Demo2 demo2) {
        this.userName = userName;
        this.age = age;
        this.demo2 = demo2;
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    @Override
    public String toString() {
        return "Demo1{" +
                "userName='" + userName + '\'' +
                ", age=" + age +
                ", s1=" + s1 +
                ", ageCone=" + ageCone +
                ", demo2=" + demo2 +
                '}';
    }
}
@Data
class Demo2 implements Cloneable{
    private String demo2Username;
    private Integer demo2Age;
    public Demo2(String demo2Username, Integer demo2Age) {
        this.demo2Username = demo2Username;
        this.demo2Age = demo2Age;
    }
}

(二) 深拷贝:

 复制出来的对象拥有和原来对象相同的一套属性值,里面的属性和被复制的对象是相互独立的,修改任何一个对象都不会对另外一个对象产生影响,


 从上面可以知道Object提供的clone方法只能实现浅拷贝,如果想实现深拷贝,可以采取以来两种方法:


 1、每个引用类型内部都实现cloneable接口并重写clone方法即可。


 2、使用序列化和反序列化(前提是类需要实现序列化接口Serializable)


 注意:序列化是将对象写到流中便于网络传输或者持久化到磁盘,而反序列化则是把对象从流/磁盘中读取出来。这里写到流中的对象则是原始对象的一个拷贝,因为原始对象还存在 JVM 中,所以我们通过对象的序列化产生克隆对象,然后通过反序列化获取这个对象。


 实现深拷贝方式一: 每个引用类型都实现Cloneable接口并重写clone方法

image.png

image.png

public class CloneDemo {
    public static void main(String[] args) throws Exception{
        Demo1 demo1 = new Demo1("1",2,new Demo2("3",4));
        System.out.println("原来的对象数据:" + demo1);
        // 深拷贝
        Demo1 cloneDemo = (Demo1) demo1.clone();
        System.out.println("拷贝出来的对象数据:" + cloneDemo);
        // 修改拷贝对象的属性
        cloneDemo.setAge(100);
        cloneDemo.setAgeCone(100);
        cloneDemo.setUserName("test");
        cloneDemo.setS1(Short.valueOf("200"));
        cloneDemo.getDemo2().setDemo2Age(1000);
        System.out.println();
        System.out.println("修改拷贝出来的对象引用类型属性:被拷贝对象的数据" + demo1);
        System.out.println("修改拷贝出来的对象引用类型属性:拷贝出来的对象数据" + cloneDemo);
    }
}
@Data
class Demo1 implements Cloneable{
    private String userName;
    private Integer age;
    private Short s1 = new Short("100");
    private Integer ageCone = new Integer(900);
    private Demo2 demo2;
    public Demo1(String userName, Integer age, Demo2 demo2) {
        this.userName = userName;
        this.age = age;
        this.demo2 = demo2;
    }
    @Override
    public String toString() {
        return "Demo1{" +
                "userName='" + userName + '\'' +
                ", age=" + age +
                ", s1=" + s1 +
                ", ageCone=" + ageCone +
                ", demo2=" + demo2 +
                '}';
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Demo1 demo1 = (Demo1) super.clone();
        Demo2 demo2 = (Demo2) demo1.getDemo2().clone();
        demo1.setDemo2(demo2);
        return demo1;
    }
}
@Data
class Demo2 implements Cloneable{
    private String demo2Username;
    private Integer demo2Age;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    public Demo2(String demo2Username, Integer demo2Age) {
        this.demo2Username = demo2Username;
        this.demo2Age = demo2Age;
    }
}

实现深拷贝方式二: 使用序列化和反序列化方式达到深拷贝

image.png

public class CloneDemo {
    public static void main(String[] args) throws Exception{
        Demo1 demo1 = new Demo1("1",2,new Demo2("3",4));
        System.out.println("原来的对象数据:" + demo1);
        // 序列化
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(demo1);
        // 反序列化
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        // 深拷贝
        Demo1 cloneDemo = (Demo1) ois.readObject();
        System.out.println("拷贝出来的对象数据:" + cloneDemo);
        // 修改拷贝对象的属性
        cloneDemo.setAge(100);
        cloneDemo.setAgeCone(100);
        cloneDemo.setUserName("test");
        cloneDemo.setS1(Short.valueOf("200"));
        cloneDemo.getDemo2().setDemo2Age(1000);
        System.out.println();
        System.out.println("修改拷贝出来的对象引用类型属性:被拷贝对象的数据" + demo1);
        System.out.println("修改拷贝出来的对象引用类型属性:拷贝出来的对象数据" + cloneDemo);
    }
}
@Data
class Demo1 implements Cloneable, Serializable {
    private String userName;
    private Integer age;
    private Short s1 = new Short("100");
    private Integer ageCone = new Integer(900);
    private Demo2 demo2;
    public Demo1(String userName, Integer age, Demo2 demo2) {
        this.userName = userName;
        this.age = age;
        this.demo2 = demo2;
    }
    @Override
    public String toString() {
        return "Demo1{" +
                "userName='" + userName + '\'' +
                ", age=" + age +
                ", s1=" + s1 +
                ", ageCone=" + ageCone +
                ", demo2=" + demo2 +
                '}';
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Demo1 demo1 = (Demo1) super.clone();
        Demo2 demo2 = (Demo2) demo1.getDemo2().clone();
        demo1.setDemo2(demo2);
        return demo1;
    }
}
@Data
class Demo2 implements Cloneable,Serializable{
    private String demo2Username;
    private Integer demo2Age;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    public Demo2(String demo2Username, Integer demo2Age) {
        this.demo2Username = demo2Username;
        this.demo2Age = demo2Age;
    }
}

二: throw和throws的区别?


 throw关键字用于主动抛出java.lang.Throwable类的一个实例化对象,当某些业务可能存在异常,但是你并不想在此处处理这个异常,可以使用throw关键字将异常抛出。如:throw new Exception(“not need to deal″)。


 throws 的作用是作为方法声明和签名的一部分(放在方法声明处),可以接受多个异常,用逗号隔开,这个方法的调用者需要处理抛出的异常或者继续使用throws将异常网上抛出,最高可抛出到JVM进行处理。Java 中,任何未处理的受检查异常强制在 throws 子句中声明。


image.png

三: 受检查异常和运行时异常与有何区别?


 受检查异常: 在编译阶段被强制检查的异常称为"受检查的异常",这种异常JAVA编译器要求必须处理,如IO异常


 运行时异常: 在编译阶段无法检测出来的,可能是由于开发者设计考虑不周全而引起的异常,这种异常只能在程序运行时才会发现,所以,处理这种异常要求开发者更加细致和有经验才能更好预测到。

image.png

四: 列举一些工作中你常遇到的运行时异常


NullPointerException (空指针异常)

IndexOutOfBoundsException (下标越界异常)

IllegalArgumentException (非法参数异常)

ClassCastException (类转换异常)

ArithmeticException(算术异常)


五: SimpleDateFormat是线程安全的吗?如果不是,怎么解决它线程不安全的问题?


 SimpleDateFormat是DateFormat 的一个实现,而DateFormat的实现都是线程不安全的,所以SimpleDateFormat 都不是线程安全的,在多线程环境下,会存在线程安全问题。


 解决: 可以将 SimpleDateFormat 存放在 ThreadLocal 中,因为ThreadLocal是线程变量,每个线程都有一个单独的ThreadLocal ,在多线程环境下也不会出现线程安全问题。


 JDK8推荐使用DateTimeFormatter来代替SimpleDateFormat ,因为它是线程安全的。



每日小结


  不积跬步,无以至千里;不积小流,无以成江海。今天播种努力的种子,总会有一天发芽!


  欢迎大家关注,如果觉得文章对你有帮助,不要忘记一键三连哦,你的支持是我创作更加优质文章的动力,希望大家都能够早日拿到心仪的Offer,有任何面试问题可以私信我,欢迎大家投稿面试题目哦!


相关文章
|
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道(带答案)
|
1月前
|
存储 缓存 Java
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
这篇文章详细介绍了Java中的IO流,包括字符与字节的概念、编码格式、File类的使用、IO流的分类和原理,以及通过代码示例展示了各种流的应用,如节点流、处理流、缓存流、转换流、对象流和随机访问文件流。同时,还探讨了IDEA中设置项目编码格式的方法,以及如何处理序列化和反序列化问题。
67 1
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
|
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
下一篇
无影云桌面