Javaのインタビューよくある質問-part3

インタビューの第3の部分FAQ

Javaの基礎

1.Java例外システム

Throwableルート、それぞれ、二つのカテゴリーに分けError、そしてException

  1. RuntimeException名前は、実際には、生産その他の異常は少し誤解を招くも、彼が実際に意味があると言われている実行している異常な科目ではない、相対的に言って、他の例外はある異常科目Errorおよびそのサブクラスは、対象例外です。
  2. 以下のために異常な被験者のプログラマが対処するために、またはコンパイル・エラー、および例外のない対象があるだろうのために、Javaが必須となり、それには、この制限はありません。

2.珍しいチェーンとは何ですか

キャッチされた例外は新しい例外を例外処理にパッケージ化して再スローします。

  1. まず、カスタム例外クラスを作成します
public class MyException extends Exception {
    public MyException(String message) {
        super(message);
    }
    public MyException(){}
}
  1. 異常なチェーンテスト
public class ExceptionChain {
    /**
     * Test1():抛出 “喝晕了” 异常
     * Test2():调用Test1(),捕获 “喝晕了” 异常,并且包装成运行时异常,继续抛出
     * main() 方法中,调用 Test2(),捕获 Test2() 方法抛出的异常
     */
    public void Test1() throws MyException {
        throw new MyException("喝车不开酒!");
    }

    public void Test2() {
        try {
            Test1();
        } catch (MyException e) {
            RuntimeException newException = new RuntimeException("司机一滴酒,亲人两行泪!");
            newException.initCause(e);
            throw newException;
        }
    }

    public static void main(String[] args) {
        ExceptionChain ec = new ExceptionChain();
        try {
            ec.Test2();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  1. 業績

多型を実装するためのメカニズムが3.Javaは何です

まず、リコール多型は何ですか?

サブクラスのオブジェクトは、親クラスの変数に割り当てられたが、それでも実行時の動作は、サブクラス特徴を示している、実行中のオブジェクトの同じタイプが異なる行動特性を示すことができることを意味します。

その言葉を忘れないでください:コンパイルは、サブクラスを見て実行し、親クラスを参照してください。

多型を達成するためのメカニズム:親クラスが2つの行為の親クラスに持つことができますサブクラスの継承、1は1が書き換えられ、頑丈です。書き換え(ランタイム多型を、真のマルチ状態)、親クラスの変数を呼び出すために、この方法を使用し、動作特性サブクラスを示すことは、再定義、重い負荷のために(コンパイル時の多型が真の多型ではありません)親クラスは、このメソッドを持っていないので、それはコンパイル時に、親クラスのメソッドがない親クラスに変数を呼び出すことはできません間違って行くだろう。

あなたは、サブクラス固有のメソッド呼び出しにしたい場合は、ダウンキャストする必要があります。

4.言う何か一般的な原理と例

ジェネリック型パラメータは、処理のデータ型が固定されていませんが、パラメータとして渡すこともあります。

  1. 基本的な

    Java 有编译器和虚拟机,编译器将 Java 源代码转换成为 .class 文件,虚拟机加载并允许 .class 文件。对于泛型类,Java 编译器会将泛型代码转换为普通的非泛型代码,将类型参数 T 擦除,替换为 Object,插入必要的强制类型转换。Java 虚拟机并不知道泛型这一回事儿,只知道普通的类及代码。

    Java 泛型是通过类型擦除来实现的,类定义中的类型参数 T 会被替换成 Object,在程序运行过程中,不知道泛型的实际类型参数,比如 Pair<Integer> ,运行只知道 Pair ,不知道 Integer

    如果指定了泛型的边界,那么在进行泛型擦除的时候,就不会转换成 Object,而是转换为边界类型。

  2. 举例说明

   public class Pair<U, V> {
       private U name;
       private V age;
   
       public Pair(U name, V age) {
           this.name = name;
           this.age = age;
       }
   
       public U getName() {
           return name;
       }
   
       public V getAge() {
           return age;
       }
   }

在经过 Java 编译之后,所以泛型类型 U,V 都转换成 Object 。在之后需要使用到的地方,都需要进行强制类型转换

Pair<String, Integer> p = new Pair<>("Hello", 20);
String name = (String)p.getName();
Integer age = (Integer)p.getAge();

5.Java 中 String 的了解

  1. 了解基本用法,基本的函数调用

  2. String 内部使用一个字符数组表示字符串,实例变量定义为:(JDK 1.9 之前)

    private final char value[];

  3. 从 JDK 1.9 开始,内部使用一个字节数组来表示字符串,实例变量定位为:

    private final byte value[]; 使用字节数组,如果字符都是 ASCII 字符,他就可以使用一个字节表示一个字符,而不是 UTF-16BE 编码,节省空间;

  4. 不可变性,一旦创建就不可变;

  5. 常量字符串,可以参考 part2 部分关于 String.intern(); 方法的讲解。

  6. 重写 hashCode 方法,参考 part1 关于 hashCode 的讲解。

  7. 运用在正则表达式中

6.String 为什么要设计成不可变的?

  1. 保证安全;
  2. 保证性能;
  3. 线程安全

7.序列化的方式

序列化就是将对象转换为字节流,反序列化就是将字节流转换为对象。

  1. 基本用法

要让一个类支持序列化,要让他实现一个接口java.io.Serializable 。对于一个学生类,我们可以如下操作使其支持序列化public class Student implement Serializable{ // 省略} 。声明之后,保存/读取Student 对象就可以通过使用 ObjectOutputStream/ObjectInputStream 流了。ObjectOutputStreamOutputStream 的子类,但是实现了ObjectOutput 接口。ObjectOutputDataOutput 的子接口,增加一个方法public void writeObject(Object obj) throws IOExceptionObjectInputStream 同理,增加一个方法public void readObject() throws ClassNotFoundException, IOException

  • 保存学生列表的代码
public static void writeStudents(List<Student> students) 
	throws IOException{
    ObjectOutputStream out = new ObjectOutputStream(
    	new BufferedOutputStream(new FileOutputStream("students.dat")));
    try {
        //out.writeInt(students.size());
        //for (Student s : students) {
        //    out.writeObject(s);
        //}
        out.writeObject(students);
    } finally {
        out.close();
    }
}
  • 从文件中读入学生列表
public static List<Student> readStudents() throws ClassNotFoundException, IOException {
    ObjectInputStream in = new ObjectInputStream(
    	new BufferedInputStream(new FileInputStream("students.dat")));
    try {
        //int size = in.readInt();
        //List<Student> list = new ArrayList<>(size);
        //for (int i = 0;i < size;i ++) {
        //    list.add( (Student) in.readObject());
        //}
        //return list;
        return (List<Student> in.readObject());
    } finally {
        in.close();
    }
}

8.如何格式化日期?

在 Java 8 中,主要的格式化类是java.time.format.DateTimeFormatter ,他是线程安全的。之前用的 java.text.SimpleDateFormat 不是线程安全的。

  1. 看一段代码,演示基本使用方法
public class DateOperation {
    public static void main(String[] args) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime ldt = LocalDateTime.of(2019,12,6,21,29,30);
        System.out.println(formatter.format(ldt));
    }
}
// 输出
// 2019-12-06 21:29:30
  1. 也可以将字符串转换为日期和时间对象,可以使用对应类的 parse 方法,如下:
public class DateOperation {
    public static void main(String[] args) {
        String string = "2019-12-06 21:29:30";
        str2DateTime(string);
    }
    public static void str2DateTime(String string) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime ldt = LocalDateTime.parse(string, formatter);
        System.out.println(ldt.toString());
    }
}
// 输出
// 2019-12-06T21:29:30

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

参考:JAVA学习篇–静态代理VS动态代理

动态代理: 一种强大的功能,他可以在运行时动态创建一个类,实现一个或多个接口,可以在不修改原有类的基础上动态为通过该类获取的对象添加方法、修改行为。是**面向切面编程(AOP)**的基础。有两种方式,一种是 Java SDK 提供的,还有一种是第三方库如 cglib。

  1. 静态代理: 由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class 文件就已经存在

    1. 优点: 代理使客户端不需要知道实现类是什么,怎么做的,只需要知道代理即可。
    2. 缺点: 代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法,出现大量的重复代码
    3. 缺点: 代理对象只服务于一种类型的对象,如果要服务多类型的对象,就需要为每一种对象都进行代理
    4. 静态代理类只能为特定的接口服务 ,如果想要为多个接口服务就要建立很多个代理类
  2. 动态代理: 在程序员运行时运用反射机制动态创建而成。

    1. 与静态代理相比,最大的优点就是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理InvocationHandler.invoke 。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。类职责更加单一,复用性更强。
    2. 使用静态代理,可以编写通用的代理逻辑,用于各种类型的被代理对象,而不需要为每一个被代理的类型都创建一个静态代理。

10.反射的原理,反射创建类实例的三种方式是什么

1.反射的原理

每个已加载的类在内存中都有一份类信息,每个对象都有指向它所属类信息的引用。类信息对应的类就是java.lang.Class

2.三种方式

  1. 使用 Object 类中的 getClass() 方法;

    Student stu = new Student();
    Class stuClass = stu.getClass();
    
  2. 任何数据类型(包括基本数据类型)都有一个 “静态” 的 class 属性

    Student stu = new Student();
    Class stuClass2 = Student.class;
    
  3. 通过 Class 类的静态方法: forName(String className) (常用)

    try {
        Class stuClass3 = Class.forName("包名.Student");
    } catch(ClassNotFoundException e) {
        e.printStackTree;
    }
    

11.说说对 Java 反射的理解

​ 反射是指在运行时,程序可以动态获取类型的信息,比如接口信息、成员信息、方法信息、构造方法信息等,根据这些动态获取的信息创建对象、访问或修改对象,调用方法。

反射就是把Java类中的各种成分映射成一个个的Java对象

Class 对象的由来是将class文件读入内存,并为之创建一个Class对象

参考如图:Java 基础之-反射

12.说说对 Java 注解的理解

注解就是给程序添加一些信息,用字符 @ 开头,这些信息用于修饰它后面紧挨着的其他代码元素,比如类、接口、字段、方法、方法中的参数、构造方法等。注解可以被编译器、程序运行时和其他工具使用,用于增强或修改程序行为。定制序列化和依赖注入容器。

  1. 内置注解

    @Override ,@Deprecated ,@SuppressWarnings(抑制的警告类型)

  2. 定制序列化和依赖注入容器是两种应用。

  3. 注解提升了Java语言的表达能力,有效实现了应用功能和底层功能的分离,框架和库的程序员可以专注于底层实现,借助反射实现通用功能,提供注解给程序员使用,应用程序员可以专注于应用功能,通过简单的声明式注解与框架或库进行协作。

  4. 可以帮助程序员执行基本编译时检查,如@Override 的使用

  5. 有四个用于修饰自定义注解的元注解:@Target 表示注解目标, @Retention 表示注解信息保留到什么时候 , @Documented 注解信息包含到生成的文档中,@Inherited

发布了16 篇原创文章 · 获赞 2 · 访问量 1272

おすすめ

転載: blog.csdn.net/yx185/article/details/103980601