「面接特別トピック - 面接で頻繁に聞かれる古典的な質問集 1」 Java 面接の鍵を解く: 面接でよく使われるよくある古典的な質問の徹底分析 (パート 1)

皆さんこんにちは。Java プログラミングが大好きなコーダー、プログラマーの Ahao です。今日は、Java の面接でよくある質問をいくつか紹介します。これらの質問を収集して分析することで、面接の準備を改善し、技術的なボトルネックを突破し、面接官を説得するのに役立つことを願っています。

第一章

1. オペレーター

  • 演算子 & と &&、| と || の違いは何ですか?
//& 按位与操作,只有对应的两个二进制数为1时,结果位才为1
1&1=1
1&0=0
0&1=0
0&0=0

//| 按位或操作,有一个为1的时候,结果位就为1
1|1=1
1|0=1
0|1=1
0|0=0

//& 和 && 都能实现 和 这个功能
//区别:& 两边都运算,而&&先算左侧,若左侧为false,那么右边就不运算,判断语句中推荐&&,效率高

//| 和 || 和上面的类型
//区别:|| 只要满足第一个条件,后面的条件就不再判断,而|要对所有条件进行判断

//把&&和||称为短路运算符

  • 最も効率的な方法で 2 × 2 の 3 乗を計算します
//原理:将一个数左移n位,就是将这个数乘以2的n次方
2 << 3 = 16

//扩展:常见的JDK源码里面HashMap的默认容量是16
int DEFAULT_INITAL_CAPACITY = 1 << 4; //16

//直接是二进制操作了,表示将1左移4位,变成10000,变成十进制就是16

  • 変数交換を実現するために 2 つの非ゼロ int 値を渡すメソッドを作成します。方法は何通りありますか?
//方式一
public static void swap(int a, int b) {
    
    
    System.out.printf("a=%d,b=%d", a, b);
    a = a + b;
    b = a - b; //b = a + b - b = a
    a = a - b; //a = a + b - a = b
    System.out.printf("a=%d,b=%d", a, b);
}

//方式二 异或运算(一个数与另一个数异或两次是其本身,一个数和自身异或结果是0)
public static void swap2(int a, int b) {
    
    
    System.out.printf("a=%d,b=%d", a, b);
    a = a ^ b; //a1 = a^b
    b = b ^ a; //b = b^a^b = a
    a = a ^ b; //a = a1^b = a^b^a = b
    System.out.printf("a=%d,b=%d", a, b);

2. データ型

  • Javaのデータ型分類について話しましょう
//基础数据类型:byte、short、int、long、float、double、char、boolean
//引用类型:其它都是引用类型
//String和Enum也是引用类型
  • 操作: 変数 int i = 5 を定義し、return i++; および return ++i; によって返される結果は何ですか?
//i++ 返回 5,先返回后增加
//++i 返回 6,先增加后返回
  • == と等しいの違い
//基本数据类型的比较,要用==判断是否相等
//引用数据类型:==比较的是内存地址是否一样,不同对象的内存地址不一样,equals比较的是具体内容,也可以自定义什么条件去判断两个对象是否一样

3. トライ、キャッチ、最後に

  • 1. 次のコードの try-catch-finally ステートメントでは、try に return とfinally に return がありますが、結果として何が返されますか? なぜ?
public static int test1() {
    
      
    int a = 1; 
    try {
    
      
        System.out.println(a / 0);  
        a = 2;  
    } catch (ArithmeticException e) {
    
      
        a = 3;  
        return a;  
    } finally {
    
      
        a = 4;  
    }  
    
    return a;  
}

public static int test2() {
    
      
    int a = 1; 
    try {
    
      
        System.out.println(a / 0);  
        a = 2;  
    } catch (ArithmeticException e) {
    
      
        a = 3;  
        return a;  
    } finally {
    
      
        a = 4;  
        return a;  
    }  
}

//test1()返回3,test2()返回4
//在执行catch中的return之前一定会先执行finally中的代码(如果有finally),如果finally中有return语句就直接执行return方法

4. リソースを試してみる

  • 1. 新しいバージョンの JDK を使用して IO ストリームを処理し、基本的なコードを作成し、あるテキスト テキストの内容を別のテキスト テキストにコピーします。
//需要关闭的资源只要实现了java.lang.AutoCloseable,就可以⾃动被关闭
//try()⾥⾯可以定义多个资源,它们的关闭顺序是最后在try()定义的资源先关闭
try (
    FileInputStream fis = new FileInputStream("/Users/lcz/Desktop/test.txt");
    BufferedInputStream bis = new BufferedInputStream(fis);
    FileOutputStream fos = new FileOutputStream("/Users/lcz/Desktop/copy.txt");
    BufferedOutputStream bos = new BufferedOutputStream(fos);
) {
    
    
    int size;
    byte[] buf = new byte[1024];
    while ((size=bis.read(buf) != -1)) {
    
    
        bos.write(buf,0,size);
    }
} catch (Exception e) {
    
    
    e.printStackTrace();
}

5. ファイル API と再帰

  • ディレクトリ内のすべてのサブディレクトリとサブファイルを検索し、コンソールに出力します。
public static void main(String[] args) {
    
    
    List<String> paths = new ArrayList<>();
    getAllFilePaths(new File("/Users/lcz/Desktop/demo"), paths);
    
    for (String path: paths) {
    
    
        System.out.println(path);
    }
}

private static void getAllFilePaths(File filePath, List<String> paths) {
    
    
    File[] files = filePath.listFiles();
    if (files == null) {
    
    
        return;
    }
    
    for (File f: files) {
    
    
        if (f.isDirectory()) {
    
    
            paths.add(f.getPath());
            getAllFilePaths(f, paths);
        } else {
    
    
            paths.add(f.getPath());
        }
    }
}

第2章

1.文字列(強調)

  • 1.String str = new String(“xxx”); オブジェクトはいくつ作成されますか?
//创建一个对象:常量池存在,则直接new一个对象
//创建两个对象:常量池不存在,则在常量池创建一个对象,在堆中也创建一个对象
  • 2. 以下の比較は何ですか? 出力は何ですか? なぜこのような結果になったのでしょうか?
String str1 = new String("s1");
String str2 = "s2";
String str3 = "s2";

System.out.println(str1 == str2); //false
System.out.println(str2 == str3); //true

//比较引用的内存地址是否一样
//第一个是false:new 创建新的对象会开辟新的内存空间,所以地址不一样
//第二个是true:都是从常量池里面获取,"s2"存在于常量池中

  • 次のコードの結果を書き留めてください。両方とも true である必要がある場合、どのように変更すればよいでしょうか?
String s1 = "s1";
String s2 = s1 + "s2"; //变量+常量 来自堆
String s3 = "s1" + "s2"; //常量+常量 来自常量池

System.out.println(s2 == "s1s2"); //false
System.out.println(s3 == "s1s2"); //true

//第⼀条语句打印的结果为false, s2 = s1 + ".net",变量+常量=堆,构建了⼀个新的string对象,并将对象引⽤赋予s2变量,常量池中的地址不⼀样,但是值⼀样。
//第⼆条语句打印的结果为true,javac编译可以对【字符串常量】直接相加的表达式进⾏优化,不⽤等到 运⾏期再去进⾏加法运算处理,⽽是直接将其编译成⼀个这些常量相连的结果

//如果需要第⼀个输出为true,只需要把变量改为常量即可 fianl String s1 = "s1"; 不管是new String("XXX")和直接常量赋值, 都会在字符串常量池创建.只是new String("XXX")⽅式会在堆中创建⼀个对象去指向常量池的对象, 普通的常量赋值是直接赋值给变量
  • String、StringBuffer、StringBuilder の違いは何ですか? どのようなシナリオで使用されますか?
//三者都是final, 不允许被继承, 本质都是char[]字符数组实现
//String、StringBuffer与StringBuilder中,String是不可变对象,另外两个是可变的

//StringBuilder 效率更快,因为它不需要加锁,不具备多线程安全
//StringBuffer ⾥⾯操作⽅法⽤synchronized ,效率相对更低,是线程安全的

//使⽤场景: 
//操作少量的数据⽤String,但是常改变内容且操作数据多情况下最好不要⽤String,因为每次⽣成中间对象性能会降低
//单线程下操作⼤量的字符串⽤StringBuilder,虽然线程不安全但是不影响
//多线程下操作⼤量的字符串,且需要保证线程安全 则⽤StringBuffer

2. オブジェクト指向

  • オブジェクト指向の 4 つの主な特徴は何ですか? 別途説明します
//抽象、封装、继承、多态

//抽象:
//关键词abstract声明的类叫作抽象类,abstract声明的⽅法叫抽象⽅法;
//⼀个类⾥包含了⼀个或多个抽象⽅法,类就必须指定成抽象类;
//抽象⽅法属于⼀种特殊⽅法,只含有⼀个声明,没有⽅法体;

//封装:
//封装是把过程和数据包围起来,对数据的访问只能通过已定义的接⼝即⽅法;
//在java中通过关键字private,protected和public实现封装;
//封装把对象的所有组成部分组合在⼀起,封装定义程序如何引⽤对象的数据,封装实际上使⽤⽅法将类的数据隐藏起来,控制⽤户对类的修改和访问数据的程度;

//继承:
//⼦类继承⽗类的特征和⾏为,使得⼦类对象具有⽗类的⽅法和属性,⽗类也叫 基类,具有公共的⽅法和属性;

//多态:
//同⼀个⾏为具有多个不同表现形式的能⼒;
//优点:减少耦合、灵活可拓展;
//⼀般是继承类或者重写⽅法实现;

3. インターフェース

  • オーバーロードとオーバーライドの違いは何ですか?
//重载Overload:表示同⼀个类中可以有多个名称相同的⽅法,但这些⽅法的参数列表各不相同,参数个数或类型不同;
//重写Override:表示⼦类中的⽅法可以与⽗类中的某个⽅法的名称和参数完全相同;
  • インターフェイスはインターフェイスから継承できますか? インターフェイスは多重継承をサポートしていますか? クラスは多重継承をサポートしていますか? インターフェイスに実装できるメソッドはありますか?
//接口里可以有静态方法和方法体
//接口中所有的方法必须是抽象方法(JDK8之后就不是了)
//接口不是要被类继承了,而是实现
//接口支持多继承,类不支持多个类继承

//一个类只能继承一个类,但是能实现多个接口,接口能继承另一个接口,接口的继承使用exstends关键字,和类继承一样
  • JDK8 の新しいインターフェース機能を理解していますか?
//interface中可以有static方法,但必须有方法体,该方法只属于接口,接口名直接调用该方法
//接口中新增default关键字修饰的⽅法,default⽅法只能定义在接⼝中,可以在⼦类或⼦接⼝中被重写,default定义的⽅法必须有⽅法体
//⽗接⼝的default⽅法如果在⼦接⼝或⼦类被重写,那么⼦接⼝实现对象、⼦类对象,调⽤该⽅法,以重写为准
//本类、接⼝如果没有重写⽗类(即接⼝)的default⽅法,则在调⽤default⽅法时,使⽤⽗类 (接⼝) 定义的default⽅法逻辑

第3章(収集フレームワーク一覧)

1. Vector、ArrayList、LinkedListの関係と違いについて教えてください。個別の使用シナリオ

//线程安全:
//ArrayList:底层是数组实现,线程不安全,查询和修改非常快,但是新增和删除慢(特殊场景除外:尾端新增和删除)
//LinkedList:底层是双向链表实现,线程不安全,查询和修改速度慢(特殊场景除外:首端的查询和修改),新增和删除速度快
//Vector:底层是数组实现,线程安全的,操作的时候用synchronized进行加锁

//使用场景:
//Vector已经很少用了
//增加和删除的场景多,用LinkedList
//查询和修改的场景多,用ArrayList

2. スレッドの安全性を確保したい場合、ArrayList は何をすべきでしょうか? 方法は何通りありますか?

//自己写一个包装类,根据业务对List操作进行加锁
//Collections.synchronizedList(new ArrayList<>());使用synchronized加锁
//CopyOnWriteArrayList<>() 使⽤ReentrantLock加锁

3. CopyOnWriteArrayList を理解していますか? スレッド セーフを実現する上で、Collections.synchronizedList との違いは何ですか?使用シナリオは何ですか?

//CopyOnWriteArrayList:执行修改操作时,会拷贝一份新的数组进行操作,代价十分昂贵,在执行完成后将原来的集合指向新的集合来完成修改操作,源码里面使用ReentrantLock可重入锁来保证不会有多个线程同时拷贝一份数组
//场景:读高性能,适用读操作远远大于写操作的场景中使用(读的时候是不需要加锁的,直接获取,删除和新增是需要加锁的,读多写少)

//Collections.synchronizedList:几乎在每个方法上就加了synchronized同步锁
//场景:写操作性能比CopyOnWriteArrayList好,读操作不如

4.CopyOnWriteArrayList の設計思想とその欠点は何ですか?

//设计思想:读写分离+最终一致
//缺点:内存占用问题,写时的复制机制,内存会同时驻扎两个对象的内存,旧对象和新的写入对象,如果对象大,则容易发生Yong GC和Full GC

5. ArrayList の展開メカニズムは何ですか?

//未指定集合容量,默认是0,若已经指定⼤⼩则集合⼤⼩为指定的
//当集合第⼀次添加元素的时候,集合⼤⼩扩容为10
//当ArrayList的元素个数⼤于其容量,扩容的⼤⼩=原始⼤⼩+原始⼤⼩/2

6. 単純な ArrayList を設計します (コンストラクター、add(e)、拡張メカニズムを含める必要があります)

//计算容量+确保容量
private void ensureCapacityInternal(int minCapacity){
    
      
    //如果是初次扩容,则使⽤默认的容量  
    if(elementData == EMPTY_ELEMENT_DATA){
    
    
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); 
    }
    //是否需要扩容,需要的最少容量⼤于现在数组的⻓度则要扩容    
    if(minCapacity - elementData.length > 0){
    
    
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //如果新容量 < 最⼩容量, 则将最新的容量赋值给新的容量
        if(newCapacity - minCapacity < 0){
    
    
            newCapacity = minCapacity;
        }
        //创建新数组
        Object[] objects = new Object[newCapacity];
        //将旧的数组复制到新的数组⾥⾯
        System.arraycopy(elementData,0,objects,0,elementData.length);
        //修改引⽤
        elementData = objects;
    }  
}

  • 重要なポイントが要約されており、読者が日々の業務の中で Java についての理解を継続的に深め、学習と実践を続けることを奨励しています。

期待と続報

これらの Java 面接の質問を共有することがあなたの役に立ち、面接の旅を強力にサポートできることを願っています。今後も同様のコンテンツを更新し続け、マップ、同時プログラミングの基本と高度な側面、ミドルウェア、データベース、通信プロトコル、フレームワークなどのより詳細なトピックを取り上げ、誰もが Java エコシステムを理解できるようにする予定です。より包括的に。

Java の面接の質問、技術的な詳細な分析、その他の側面について提案や期待がある場合は、コメント領域で共有してください。皆様のフィードバックに基づいて、関心のあるトピックに優先順位を付け、内容が実践的で深いものになるようにいたします。

読んでくれてありがとう

最後に、時間を割いて私のブログを読んでくださった皆様、ありがとうございました。この種のコンテンツが気に入った場合は、忘れずに私のブログをフォローしてください。私たちはテクノロジーの海で前進し、共に成長し続けます。

Guess you like

Origin blog.csdn.net/weixin_44976692/article/details/135145322