关闭

Java经典面试题(其一)——Java异常和克隆

标签: 面试题javaexceptionclone
571人阅读 评论(0) 收藏 举报
分类:

Java经典面试题(其一)——Java异常和克隆

谈一谈Java中的Error和Exception

1.Error和Exception的联系

继承关系:Error和Exception都是继承于Throwable,RuntimeException继承自Exception。

Error和RuntimeExceptime及其子类被称为未检查异常(Unchecked exception),其它异常称为受检查异常(Checked Exception)。

2.Error和Exception的区别

Error类一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存不足,方法调用栈溢出等。如Java.lang.StackOverFlowError和Java.lang.OutOfMemoryError。对于这类错误,Java编译器不去检查他们。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和预防,遇到这样的错误,建议让程序终止。

Exception类表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能的处理异常,使程序恢复运行,而不应该随意终止异常。

3.运行时异常和受检查异常

Exception又分为运行时异常(Runtime Exception)和受检查的异常(Checked Exception)。

RuntimeException:其特点是Java编译器不去检查它,也就是说,当程序中可能出现这类异常时,即时没有用try……catch捕获,也没有用throws抛出,还是会编译通过,如除数为零的ArithmeticException、错误的类型转换、数组越界访问和试图访问空指针等。处理RuntimeException的原则是:如果出现RuntimeException,那么一定是程序员的错误。

受检查异常(IOException等):这类异常如果没有try……catch也没有throws抛出,编译器是通不过的。这是在应用环境中出现的外部错误。

4.throw和throws两个关键字有什么不同

throw是用来抛出任意异常的,你可以抛出任意Throwable,包括自定义的异常类对象;throws总是出现在一个函数头中,用来标明该成员函数可能抛出的各种异常。如果方法抛出了异常,那么调用这个方法的时候就需要处理这个异常。

5.try-catch-finally-return执行顺序

1> 不管是否有异常产生,finally块中代码都会执行;

2> 当try和catch中有return语句时,finally块仍然会执行;

3> finally是在return后面的表达式运算执行的,所以函数返回值在finally执行前确定的,无论finally中的代码怎么样,返回的值都不会改变,仍然是之前return语句中保存的值;

4> finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值。

6.常见的几种RunException

一般面试中Java Exception(RuntimeExceptime)是必会问道的问题,常见的异常列出四五种,是基本要求。

常见的几种如下:

NullPointerException - 空指针引用异常

ClassCastException - 类型强制转换异常

IllegalArgumentException - 传递非法参数异常

ArithmeticException - 算术运算异常

ArrayStoreException - 向数组中存放与声明类型不兼容对象异常

IndexOutOfBoundsException - 下标越界异常

NegativeArraySizeException - 创建一个大小为负数的数组错误异常

NumberFormatException - 数字格式异常

SecurityException - 安全异常

UnsupportedOperationException - 不支持的操作异常

NegativeArrayException - 数组负下标异常

EOFException - 文件已结束异常

FileNotFoundException - 文件未找到异常

SQLException - 操作数据库异常

IOException - 输入输出异常

NoSuchMethodException - 方法未找到异常

java.lang.AbstractMethodError - 抽象方法错误。当应用试图调用抽象方法时抛出。

java.lang.AssertionError - 断言错。用来指示一个断言失败的错误。

java.lang.ClassCircularityError - 类循环依赖错误。在初始化一个类时,若检测到类之间循环依赖则抛出该异常。

java.lang.ClassFormatError - 类格式错误。当Java虚拟机试图从一个文件中读取Java类,而检测到该文件的内容不符合类的有效格式时输出。

java.lang.Error - 错误。是所有错误的基类,用于标识严重的程序运行问题。这些问题通常描述一些不应被应用程序捕获的反常情况。

详解Java中的对象克隆

1.在Java语言中,我们说两个对象是否相等通常有两层含义:

对象的内容是否相等,通常使用到对象equals(Object o)函数;

引用的地址是否相同,使用运算符==比较即可。

当两个对象通过赋值符号 = 赋值时,表明这两个对象指向了内存中同一个地址,所以改变其中一个对象的内容,也就是间接改变了另一个对象的内容。有时候,我们需要从一个已经存在的对象重新拷贝一份出来,并且不仅这两个内容相等,在内存中存在两个独立的存储地址,互不影响,这时,就需要用到Java的克隆机制。

2.Cloneable

通过Cloneable接口可以很轻松地实现Java对象的克隆,只需要implements Cloneable并实现Object的clone()方法即可,如:

public class User implements Cloneable{

    private String username;

    private String password;

    public User(String username, String password) {
        super();
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public boolean equals(Object obj) {
        User user = (User) obj;
        if (username.equals(user.username) && password.equals(user.password)) {
            return true;
        }
        return false;
    }

}

// 注意这里对象实现的是Object的clone方法,因为Cloneable是一个空接口

package java.lang;

/**
 * A class implements the <code>Cloneable</code> interface to
 * indicate to the {@link java.lang.Object#clone()} method that it
 * is legal for that method to make a
 * field-for-field copy of instances of that class.
 * <p>
 * Invoking Object's clone method on an instance that does not implement the
 * <code>Cloneable</code> interface results in the exception
 * <code>CloneNotSupportedException</code> being thrown.
 * <p>
 * By convention, classes that implement this interface should override
 * <tt>Object.clone</tt> (which is protected) with a public method.
 * See {@link java.lang.Object#clone()} for details on overriding this
 * method.
 * <p>
 * Note that this interface does <i>not</i> contain the <tt>clone</tt> method.
 * Therefore, it is not possible to clone an object merely by virtue of the
 * fact that it implements this interface.  Even if the clone method is invoked
 * reflectively, there is no guarantee that it will succeed.
 *
 * @author  unascribed
 * @see     java.lang.CloneNotSupportedException
 * @see     java.lang.Object#clone()
 * @since   JDK1.0
 */
public interface Cloneable {
}

// 从源码中可以看出,需要实现Object类中的clone()方法(注意:clone()方法是一个native方法,同时抛出了一个异常)

/**
     * Creates and returns a copy of this object.  The precise meaning
     * of "copy" may depend on the class of the object. The general
     * intent is that, for any object {@code x}, the expression:
     * <blockquote>
     * <pre>
     * x.clone() != x</pre></blockquote>
     * will be true, and that the expression:
     * <blockquote>
     * <pre>
     * x.clone().getClass() == x.getClass()</pre></blockquote>
     * will be {@code true}, but these are not absolute requirements.
     * While it is typically the case that:
     * <blockquote>
     * <pre>
     * x.clone().equals(x)</pre></blockquote>
     * will be {@code true}, this is not an absolute requirement.
     * <p>
     * By convention, the returned object should be obtained by calling
     * {@code super.clone}.  If a class and all of its superclasses (except
     * {@code Object}) obey this convention, it will be the case that
     * {@code x.clone().getClass() == x.getClass()}.
     * <p>
     * By convention, the object returned by this method should be independent
     * of this object (which is being cloned).  To achieve this independence,
     * it may be necessary to modify one or more fields of the object returned
     * by {@code super.clone} before returning it.  Typically, this means
     * copying any mutable objects that comprise the internal "deep structure"
     * of the object being cloned and replacing the references to these
     * objects with references to the copies.  If a class contains only
     * primitive fields or references to immutable objects, then it is usually
     * the case that no fields in the object returned by {@code super.clone}
     * need to be modified.
     * <p>
     * The method {@code clone} for class {@code Object} performs a
     * specific cloning operation. First, if the class of this object does
     * not implement the interface {@code Cloneable}, then a
     * {@code CloneNotSupportedException} is thrown. Note that all arrays
     * are considered to implement the interface {@code Cloneable} and that
     * the return type of the {@code clone} method of an array type {@code T[]}
     * is {@code T[]} where T is any reference or primitive type.
     * Otherwise, this method creates a new instance of the class of this
     * object and initializes all its fields with exactly the contents of
     * the corresponding fields of this object, as if by assignment; the
     * contents of the fields are not themselves cloned. Thus, this method
     * performs a "shallow copy" of this object, not a "deep copy" operation.
     * <p>
     * The class {@code Object} does not itself implement the interface
     * {@code Cloneable}, so calling the {@code clone} method on an object
     * whose class is {@code Object} will result in throwing an
     * exception at run time.
     *
     * @return     a clone of this instance.
     * @throws  CloneNotSupportedException  if the object's class does not
     *               support the {@code Cloneable} interface. Subclasses
     *               that override the {@code clone} method can also
     *               throw this exception to indicate that an instance cannot
     *               be cloned.
     * @see java.lang.Cloneable
     */
    protected native Object clone() throws CloneNotSupportedException;

// 从clone()函数的注释中能够看出对象与克隆对象之间的关系,测试代码如下(注意:我们在User对象中重写了equals()函数)

public static void main(String[] args) throws CloneNotSupportedException{
        User userOne, userTwo, userThree;
        userOne = new User("username", "password");
        userTwo = userOne;
        userThree = (User) userOne.clone();

        System.out.println(userTwo==userOne);            //true
        System.out.println(userTwo.equals(userOne));    //true

        System.out.println(userThree==userOne);            //false
        System.out.println(userThree.equals(userOne));    //true

    }

// 测试结果显示,通过clone()函数,我们成功地从userOne对象中克隆出来一份独立的userThree对象
  1. 浅克隆与深克隆
// 谈此之前,我们先看一个例子,定义一个名为Company的类,并添加一个类型为User的成员变量

public class Company implements Cloneable{

    private User user;

    private String address;

    public Company(User user, String address) {
        super();
        this.user = user;
        this.address = address;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public boolean equals(Object obj) {
        Company company = (Company) obj;
        if (user.equals(company.getUser()) && address.equals(company.address)) {
            return true;
        }
        return false;
    }

}

// 测试代码及测试结果如下

public static void main(String[] args) throws CloneNotSupportedException{
        Company companyOne, companyTwo, companyThree;
        companyOne = new Company(new User("username", "password"), "上海市");
        companyTwo = companyOne;
        companyThree = (Company) companyOne.clone();

        System.out.println(companyTwo==companyOne);                //true
        System.out.println(companyTwo.equals(companyOne));        //true

        System.out.println(companyThree==companyOne);            //false
        System.out.println(companyThree.equals(companyOne));    //true

        System.out.println(companyThree.getUser()==companyOne.getUser());            //true ? 这里为什么不是false呢
        System.out.println(companyThree.getUser().equals(companyOne.getUser()));    //true

    }

问题来了,companyThree与companyOne的User是同一对象!也就是说companyThree只是克隆了companyOne的基本数据类型的数据,而对于引用的数据没有进行深度的克隆。也就是俗称的浅克隆。

浅克隆:顾名思义,就是很表层的克隆,只克隆对象自身的引用地址;

深克隆:也称“N层克隆”,克隆对象自身以及对象所包含的引用类型对象的引用地址。

这里需要注意的是,对于基本数据类型(primitive)和使用常量池方式创建的String类型,都会针对原值克隆,所以不存在引用地址一说。当然不包括他们对应的包装类。

所以使用深克隆可以解决上述Company类的Clone()函数:

@Override
    protected Object clone() throws CloneNotSupportedException {
        Company company = (Company) super.clone();
        company.user = (User) company.getUser().clone();
        return company;
    }

// 再运行测试代码,就能得到companyThree.getUser()==companyOne.getUser() 为false的结果

4.Serializable实现

通过上述介绍,我们知道,实现一个对象的克隆,需要如下几步:

对象所在的类实现Cloneable接口;

2.重写clone()函数,如果包涵引用类型的成员变量,需要使用深克隆。

如果对象不包含引用类型成员或者数量少的话,使用Cloneable接口还能接受,但当对象包含多个引用类型的成员,同时这些成员又包含了引用类型的成员,那层层克隆岂不是相当繁琐,并且维护不便?所以,这里接受一种更加方便的实现方式,使用ObjectOutputStream和ObjectInputStrean来实现对象的序列化和反序列化

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public abstract class BeanUtils {
    @SuppressWarnings("unchecked")
    public static <T> T cloneTo(T src) throws RuntimeException {
        ByteArrayOutputStream memoryBuffer = new ByteArrayOutputStream();
        ObjectOutputStream out = null;
        ObjectInputStream in = null;
        T dist = null;
        try {
            out = new ObjectOutputStream(memoryBuffer);
            out.writeObject(src);
            out.flush();
            in = new ObjectInputStream(new ByteArrayInputStream(memoryBuffer.toByteArray()));
            dist = (T) in.readObject();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (out != null)
                try {
                    out.close();
                    out = null;
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            if (in != null)
                try {
                    in.close();
                    in = null;
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
        }
        return dist;
    }
}

// 只要要克隆的对象以及对象所包含的引用类型的成员对象所在的类实现了java.io.Serializable 接口即可做到完美克隆。
1
0
查看评论

java异常面试题及编程题

概念题 1.java中用来抛出异常的关键字是什么? throw 2.异常和Error的区别? error:是不可捕捉到的,无法采取任何恢复的操作,顶多只能显示错误信息。 Exception :表示可恢复的例外,这是可捕捉到的 3.什么是异常? 所谓异常是指程序在运行过程中发生的一些不正常事件。(如...
  • Bubble1210
  • Bubble1210
  • 2016-03-01 22:12
  • 3547

Java异常处理面试题归纳

对常见关于exception面试题的归纳
  • hai_cheng001
  • hai_cheng001
  • 2014-07-22 10:53
  • 2713

Java异常相关的面试题(上)

1)java中什么是Exception ?        简单的说,异常就是Java传达给系统的和程序错误的方式。在java中,异常功能是通过实现比如 Throwable,Exception,RuntimeException 之类的,然后还有一些处理异常时候的...
  • supersanya
  • supersanya
  • 2016-03-14 17:29
  • 1895

Java异常的面试问题及答案

本文由 ImportNew - 韩远青 翻译自 Journaldev。欢迎加入Java小组。转载请参见文章末尾的要求。 Java提供了一个健壮的、面向对象的方法来处理出现异常,称为Java异常处理。 我以前写过一篇长文章来介绍Java异常处理,...
  • moudaen
  • moudaen
  • 2014-05-12 11:02
  • 1101

Java基础(二):Object、Class、克隆、异常编程

本篇接上篇继续介绍java编程中两个重要的类Object 和 Class及java异常编程 (一)java.lang.Object 顾名思义,对象之义;java是对向对象的编程,万事万物皆对象皆是java编程的心诀。 (1)是所有Java类(API提供的类、自定义类)的最终父类. (2)作用...
  • chenchaoqingtian
  • chenchaoqingtian
  • 2016-02-24 11:47
  • 641

java中5种常见的异常(Exception)

1. Java.lang.NullPointerException   这个异常大家肯定都经常碰到,异常的解释是"程序遇上了空指针",简单地说就是调用了未经初始化的对象或者是不存在的对象,这个错误经常出现在创建图片,调用数组这些操作中,比如图片未经初始化,或者图片创建时的路径错...
  • qq_27292113
  • qq_27292113
  • 2016-03-31 13:57
  • 17258

【Java面试知识】异常

Java中的两种异常类型是什么?他们有什么区别?Java中有两种异常:受检查的(checked)异常和不受检查的(unchecked)异常。不受检查的异常不需要在方法或者是构造函数上声明,就算方法或者是构造函数的执行可能会抛出这样的异常,并且不受检查的异常可以传播到方法或者是构造函数的外面。相反,受...
  • liyazhou0215
  • liyazhou0215
  • 2017-08-19 16:36
  • 233

JAVA Exception异常总结(JAVA Exception面试笔试总结)

这方面主要有以下几种类型的题目: 1 try中throw一个exception,能否直接catch? public void method1(){ try{ throw new StringIndexOutOfBoundsException("method1 catc...
  • magic_wz
  • magic_wz
  • 2012-12-18 16:08
  • 1878

非常经典的JAVA编程题全集(50题及答案)

  • 2014-12-24 16:56
  • 116KB
  • 下载

java常见的120道面试题第一季(每30题为一季,整理的好辛苦~~~)

哎,大晚上的看题,也是醉了,分享给大伙咯!!! 问答题1 /120 什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”? 参考答案 Java虚拟机是一个可以执行Java字节码的虚拟机进程。Java源文件被编译成能被Java虚拟机执行的字节码文件。 Java被设计成允许应用程...
  • Summer_YuXia
  • Summer_YuXia
  • 2016-11-25 23:37
  • 1944
    个人资料
    • 访问:77659次
    • 积分:1575
    • 等级:
    • 排名:千里之外
    • 原创:65篇
    • 转载:0篇
    • 译文:0篇
    • 评论:45条
    博客专栏
    最新评论