Java八股文——Java基础篇

1、你是怎样理解OOP[面向对象]

面向对象是利用语言对现实事物进行抽象。面向对象具有以下特征:

  1. 继承:继承是从已有类得到继承信息创建新类的过程
  2. 封装:封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口
  3. 多态:多态是指允许不同子类型的对象对同一消息作出不同的响应

2、重载和重写的区别

  1. 重载发生在本类,重写发生在父类与子类之间
  2. 重载的方法名必须相同,重写的方法名相同且返回值类型必须相同。
  3. 重载的参数列表不同,重写的参数列表必须相同
  4. 重写的访问权限不能比父类中被重写的方法的访问权限更低。
  5. 构造方法不能被重写

3、接口与抽象类的区别

  1. 抽象类要被子类继承,接口要被类实现
  2. 接口可多继承接口,但类只单继承
  3. 抽象类可以有构造器、接口不能有构造器
  4. 抽象类:除了不能实例化抽象类之外,它和普通Java类没有任何区别
  5. 抽象类抽象方法可以有public、protected和default这些修饰符、接口:只能是public
  6. 抽象类:可以有成员变量;接口:只能声明常量

4、[深拷贝]与浅拷贝的理解

深拷贝和浅拷贝就是指对象的拷贝,一个对象中存在两种类型的属性,一种是基本数据类型,一种是实例对象的引用。

  1. 浅拷贝是指,只会拷贝基本数据类型的值,以及实例对象的引用地址,并不会复制一份引用地址所指向的对象,也就是浅拷贝出来的对象,内部的类属性指向的是同一个对象
  2. 深拷贝是指,既会拷贝基本数据类型的值,也会针对实例对象的引用地址所指向的对象进行复制,深拷贝出来的对象,内部的类指向的不是同一个对象。
  • 浅拷贝实现方式
  • 深拷贝实现方式

5、sleep和wait区别

方法定义常见用途
wait()释放锁并进入等待状态,直到被其他线程 notify() 或 notifyAll() 唤醒线程间协作,等待某个条件的发生
sleep()暂停当前线程一段时间,但不释放锁模拟延迟,控制线程执行节奏

主要区别

特性wait()sleep()
锁释放释放当前对象锁不释放锁
调用位置必须在同步块 (synchronized) 中调用可以在任何地方调用
所属类Object 类Thread 类
中断行为会抛出 InterruptedException会抛出 InterruptedException
唤醒机制需要 notify() 或 notifyAll()自动恢复,无需唤醒
状态进入 WAITING 或 TIMED_WAITING 状态进入 TIMED_WAITING 状态
  • sleep方法
  • wait方法

6、什么是自动拆装箱,int和Integer有什么区别

自动拆装箱

自动装箱(Autoboxing)  和 自动拆箱(Unboxing)  是 Java 为了简化基本类型和包装类型之间的转换而引入的机制。java为什么要引入自动装箱和拆箱的功能?主要是用于java集合中,List<Inteter> list=new ArrayList<Integer>();list集合如果要放整数的话,只能放对象,不能放基本类型,因此需要将整数自动装箱成对象。

  • 自动装箱(Autoboxing) :将 基本类型 转换为 对应的包装类型int primitiveInt = 10; Integer wrappedInt = primitiveInt;
  • 自动拆箱(Unboxing) :将 包装类型 转换为 对应的基本类型Integer wrappedInt = 20; int primitiveInt = wrappedInt;

int和Integer的区别

特性intInteger
类型基本数据类型包装类(java.lang.Integer
内存分配栈内存堆内存
初始值0null
存储范围-2^31 到 2^31-1-2^31 到 2^31-1
性能更高较低
方法包含常用方法,如 parseInt()toString()
比较== 比较值== 比较引用,.equals() 比较值

Integer缓存机制

Integer 在 -128 到 127 之间的值会 缓存,在这个范围内的 Integer 对象会复用已有实例,超出这个范围的值则会创建新的实例。

7、==和equals区别

特性==equals()
比较内容比较引用地址(基本类型比较值)默认比较引用地址,可重写后比较
适用类型基本数据类型和引用类型仅适用于引用类型
性能更快较慢(涉及方法调用)
可重写性不可重写可在类中重写
空指针安全安全可能抛出 NullPointerException

String特殊情况

  • 虽然 String 是引用类型,但在某些情况下可以直接使用 == 进行比较,这是因为 String 具有常量池机制。但String还是建议使用.equals()去比较字符串,也可使用Objects.equals()避免空指针异常。

8、String能被继承吗?为什么使用final修饰

  • String不能被继承,因为它是一个final修饰的类

为什么String被final修饰

  1. 保证字符串的不可变性
  2. 提高字符串常量池的效率
  3. String 类中有native关键字修饰的调用系统级别的本地方法,调用了操作系统的 API,如果方法可以重写,可能被植入恶意代码,破坏程序。Java 的安全性也体现在这里。

9、StringBuffer和StringBuilder的区别

特性StringBufferStringBuilder
线程安全性线程安全(方法同步)非线程安全
性能较慢(同步开销)较快
适用场景多线程环境单线程环境
初始版本JDK 1.0JDK 1.5
常用方法append()insert()delete()reverse()append()insert()delete()reverse()
是否可变可变可变

10、final、finally、finalize?

  • **final:**修饰符(关键字)有三种用法:修饰类、变量和方法。修饰类时,意味着它不能再派生出新的子类,即不能被继承,因此它和abstract是反义词。修饰变量时,该变量使用中不被改变,必须在声明时给定初值,在引用中只能读取不可修改,即为常量。修饰方法时,也同样只能使用,不能在子类中被重写。
  • **finally:**通常放在try…catch的后面构造最终执行代码块,这就意味着程序无论正常执行还是发生异常,这里的代码只要JVM不关闭都能执行,可以将释放外部资源的代码写在finally块中。
  • **finalize:**Object类中定义的方法,Java中允许使用finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在销毁对象时调用的,通过重写finalize() 方法可以整理系统资源或者执行其他清理工作。

11、Object中有哪些方法?

  1. protected Object clone()——创建并返回此对象的一个副本。
  2. boolean equals(Object obj)——指示某个其它对象是否与此对象相等。
  3. protected void finalize()——当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
  4. Class<? extends Object> getClass()——返回一个对象的运行时类。
  5. int hashCode()——返回该对象的哈希码值。
  6. void notify()——唤醒在此对象监视器上等待的单个线程。
  7. void notifyAll()——唤醒在此对象监视器上等待的所有线程。
  8. String toString()——返回该对象的字符串表示。

12、集合体系

在这里插入图片描述

java.util.Collection (顶层接口)
├── java.util.List(有序、可重复)
│   ├── java.util.ArrayList
│   ├── java.util.LinkedList
│   └── java.util.Vector
│       └── java.util.Stack
├── java.util.Set(无序、不可重复)
│   ├── java.util.HashSet
│   ├── java.util.LinkedHashSet
│   └── java.util.TreeSet
└── java.util.Queue(队列,FIFO)
    ├── java.util.PriorityQueue
    └── java.util.Deque(双端队列)
        ├── java.util.LinkedList
        └── java.util.ArrayDeque

java.util.Map(键值对)
├── java.util.HashMap
├── java.util.LinkedHashMap
├── java.util.TreeMap
└── java.util.Hashtable

AI写代码mathematica
123456789101112131415161718192021

13、ArrayList和LinkedList区别

  1. ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
  2. 对于随机访问get和set,ArrayList效率优于LinkedList,因为LinkedList要移动指针。
  3. 对于新增和删除操作add和remove,LinekdList比较占优势,因为ArrayList要移动数据。这一点要看实际情况的。只对单条数据插入或删除,ArrayList的速度反而优于LinkedList。但若是批量随机的插入删除数据,LinkedList的速度大大优于ArrayList,因为ArrayList每插入一条数据,要移动插入点之后的所有数据。

14、HashMap底层是什么,为什么要用这几类结构

  • HashMap的底层是数组+链表+红黑树(JDK 8+)
HashMap (JDK 8+)
├── 数组(Node<K, V>[])
│   ├── 链表
│   └── 红黑树
└── 散列函数(hash())

AI写代码scss
12345
  • Node<K, V>结构
  • 为什么要使用数组+链表+红黑树

数组:查询效率高,O(1)时间复杂度;

链表:处理哈希冲突,适合小规模冲突;

红黑树:当链表长度超过8时,转换为红黑树,提高查询效率。

  • 哈希函数
static final int hash(Object key) {
    int h;
    //异或高位扰动,减少哈希冲突
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

AI写代码java
运行
12345

15、HashMap和HashTable的区别

特性HashMapHashtable
线程安全非线程安全线程安全
效率
Null 键和值允许 1 个 null 键,多个 null 值不允许 null 键和 null 值
初始容量1611
容量要求容量一定是2的整数幂不一定是2的整数幂
扩容机制容量翻倍容量翻倍 + 1
负载因子0.75(默认)0.75(固定)
遍历方式IteratorEnumerator
底层实现数组 + 链表 + 红黑树数组 + 链表
JDK 版本JDK 1.2JDK 1.0
推荐使用大部分场景兼容旧代码

16、线程的创建方式

方式

  1. 继承 Thread 类,重写 Thread 类的 run() 方法,通过 start() 方法启动线程
// 继承 Thread 实现线程
class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " - Count: " + i);
        }
    }
}

public class ThreadTest {
    public static void main(String[] args) {
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();

        thread1.start();
        thread2.start();
    }
}

AI写代码java
运行
12345678910111213141516171819

2. 实现 Runnable 接口,实现 Runnable 接口的 run() 方法,通过 Thread 构造函数传入 Runnable 实例

// 实现 Runnable 接口
class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " - Count: " + i);
        }
    }
}

public class RunnableTest {
    public static void main(String[] args) {
        Thread thread1 = new Thread(new MyRunnable());
        Thread thread2 = new Thread(new MyRunnable());

        thread1.start();
        thread2.start();
    }
}


AI写代码java
运行
1234567891011121314151617181920

3. 实现 Callable 接口(带返回值),实现 Callable 接口的 call() 方法,使用 FutureTask 包装 Callable 实例,通过 Thread 启动

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        Thread.sleep(1000);
        return Thread.currentThread().getName() + " - Task Completed";
    }
}

public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> task = new FutureTask<>(new MyCallable());
        Thread thread = new Thread(task);
        thread.start();

        // 阻塞等待线程执行完毕
        String result = task.get();
        System.out.println(result);
    }
}

AI写代码java
运行
1234567891011121314151617181920212223

4. 使用线程池(ExecutorService),使用 Executors 创建线程池,提交任务 (submit() / execute())

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolTest {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(3);

        for (int i = 0; i < 5; i++) {
            executor.execute(() -> {
                System.out.println(Thread.currentThread().getName() + " is running");
            });
        }

        executor.shutdown();
    }
}

AI写代码java
运行
12345678910111213141516

5. 使用匿名内部类

public class AnonymousThreadTest {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName() + " - Count: " + i);
            }
        });

        thread.start();
    }
}

AI写代码java
运行
1234567891011

6. 使用Lambda表达式

public class LambdaThreadTest {
    public static void main(String[] args) {
        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName() + " - Count: " + i);
            }
        }).start();
    }
}


AI写代码java
运行
12345678910

对比

方式优点缺点
继承 Thread代码简单,易于理解不能继承其他类
实现 Runnable可实现多继承,解耦逻辑无返回值
实现 Callable支持返回值和异常处理代码稍复杂
线程池高效管理线程,复用线程资源需要手动关闭线程池
匿名内部类简化代码结构可读性较差
Lambda简洁、高效只能用于函数式接口
本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:[email protected]