Java基础

一、面向对象的知识要点

面向对象编程(Object-Oriented Programming,OOP)是一种基于对象概念的编程思想,它将代码中的数据和行为打包为对象,并通过这些对象之间的交互来实现功能。面向对象编程具有封装、继承和多态等特性,其主要知识点如下:

  1. 类与对象:类是具有相似属性和行为的对象集合的模板,对象则是类的实例化结果。在类中可以定义成员变量、方法和构造函数。

  2. 封装:封装指的是将类的内部实现细节隐藏起来,只对外暴露必要的接口,以保证数据的安全性和完整性。常见的封装机制包括访问修饰符合getter/setter方法等。

  3. 继承:继承是指子类可以继承父类的属性和方法,并在此基础上进行扩展和修改。继承可以有效地提高代码的复用性和可维护性。

  4. 多态:多态是指同一个类型的对象可以使用不同的表现形式,同时也允许不同类型的对象对同一消息进行响应。多态可以通过接口、抽象和方法重写等实现。

  5. 抽象类与接口:抽象类是一种不能被实例化的类。用于定义公共的属性和方法,而接口则是一种定义规范化的机制,用于实现类之间的解耦和多态。抽象类和接口可以共同实现类的多重继承。

  6. 构造函数与静态代码块:构造函数用于在创建对象时初始化成员变量等操作,而静态代码块则是在类加载时只执行一次的代码块,可以用于初始化静态变量等操作。

  7. Exception与异常处理:Exception是Java中的异常类,用于表示程序运行过程中出现的一些不正常情况。Java中提供了try-catch-finally语句和throws关键字等方式来处理异常,以保证程序的健壮性和稳定性。

  8. 重写与重载,重写(Override)是指子类对父类中的方法进行重新定义,以满足自己的需求;重载(Overload)则是指同一个类中定义多个同名但参数列表不同的方法,以增强代码的灵活性和可读性。

二、设计模式的单例模式,要求线程是安全的

单例模式(Singleton Pattern)是以一种常见的设计模式,其目的是确保一个类只有一个实例,并提供全局访问点。在Java中,单例模式可以通过私有构造函数、静态方法和静态变量等实现。线程安全的单例模式可以通过双重检查锁定(Double-Check Locking)来实现,代码如下所示:

public class Singleton{
    private volatile static Singleton instance; //私有静态变量,volatile关键字确保可见性和禁止指令重排
    
    private Singleton(){}   //私有构造函数
    
    public static Singleton getInstance(){
        if(instance == null){   //第一次检查,避免不必要的同步
            synchronized(Singleton.class){
               if(instance == null){    //第二次检查,确保线程安全
                   instance = new Singleton();
               }
            } 
        }
        return instance;
    }
}

在上述代码中,私有构造函数和私有静态变量确保了Singleton类只能被自身内部实例化,getInstance()方法则提供了获取单例实例的途径。在getInstance()方法中,我们进行了双重检查:第一次检查用于避免已经存在实例的操作情况,提高了代码效率;第二次检查则是为了确保线程安全,只有在实例不存在时才进行同步操作。

由于使用了volatile关键字,保证了可见性和禁止指令重排。这种方式既支持懒加载,又能够保证线程安全,是一种比较常见的单利模式实现方式。

三、使用ArrayList去除集合中字符串的重复值,字符串的内容相同

可以通过使用HashSet来去除集合中字符串的重复值,HashSet是一个无序不重复元素的集合,当向其中加入元素时,如果发现该元素已经存在则不作任何操作。我们可以利用这个特性将原来的ArrayList转换成HashSet再转换回ArrayList即可去除重复元素,示例代码如下:

import java.util.ArrayList
import java.util.HashSet
    
public class Demo{
    public static void main(String[] args){
        ArrayList<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("a");
        list.add("b");
        
        HashSet<String> set = new HashSet<>(list);  //将ArrayList转换为HashSet
        list.clear();   //清空原来的ArrayList
        list.addAll(set);   //将去重后的内容重新添加到ArrayList中
        
        System.out.println(list);
    }
}

在上述代码中,我们先创建了一个包含重复元素的ArrayList,然后将其转换成HashSet,并清空原来的ArrayList,最后将去重后的内容重新添加到ArrayList中。运行结果如下:

[a,b,c,d]

可以看出,重复元素"a"和"b"已经被去除,只剩下了不重复的元素"a"、"b"、"c"和"d"

四、HashMap、LinkedList、TreeMap、Hashtable之间的区别

HashMap、LinkedList、TreeMap和Hashtable这四种数据结构都是用于存储和管理键值对数据的,但它们之间有一些不同之处:

  1. HashMap:使用哈希表实现,允许为null键和null值,不保证元素的顺序,HashMap是非同步的,而且非线程安全。HashMap在插入和查询方面具有很高的效率,适用于没有特殊要求的场景。

  2. LinkedList:是一个双向链表,它可以用作队列和栈,它允许所有元素(包括null),LinkedList是非同步的,而且非线程安全。LinkedList适用于需要在集合首尾添加或删除元素的场景,在中间位置去增删元素相对较慢。

  3. TreeMap:是一种基于红黑树(Red-Black tree)实现的有序映射。TreeMap中的键值对按照键的顺序进行排序,TreeMap不允许为null键,但允许为null值,TreeMap是非同步的,而且非线程安全。TreeMap的插入和查询效率比HashMap略低,但在有序遍历方面具有优势。

  4. Hashtable:是基于哈希表的Map接口的同步实现。它不允许null键和null值。与HashMap相比,Hashtable在任何情况下都是线程安全的,但效率较低。

五、HashSet、LinkedHashSet、TreeSet之间的区别

HashSet、LinkedHashSet和TreeSet都是Java中常用的Set接口的实现,它们之间的主要区别如下:

  1. HashSet:HashSet基于HashMap实现,数据无序,允许存储null值,不允许重复元素,插入和查询速度快。HashSet是非同步的,所以是非线程安全的。

  2. LinkedHashSet:LinkedHashSet是HashSet的一个子类,它使用双向链表维护元素的插入顺序,因此插入顺序和遍历顺序相同。它允许存储null值,且不允许重复元素。LinkedHashSet是非同步的,所以是非线程安全的。

  3. TreeSet:TreesSet是基于TreeMap实现的有序集合,排序的方式可以是自然排序(如数字和字母排序)或者根据提供的Comparator进行排序。插入速度和查询速度比HashSet、LinkedHashSet略慢,但当涉及有序排列时,TreeSet比HashSet更有效率。TreeSet不允许存储null值,且不允许重复元素。TreeSet是非同步的,所在是非线程安全的。

六、String、StringBuffer、StringBuilder之间的区别

  1. String:String对象是不可变的,一旦创建就不能修改。任何对String对象的操作都不会改变原对象,而是返回一个新的字符串对象。这是因为字符串池中的字符串是共享的,不允许被修改。

  2. StringBuffer:是可变字符串,它可以动态地修改字符串。StringBuffer是线程安全的,在多线程环境中,多个对象可以同时(并发地)修改StringBuffer对象而不会产生问题。因此适用于多线程环境下的字符串操作。

  3. StringBuilder:同样是可变的字符串类,但它不是线程安全的。在多线程环境中,多个线程不能同时修改StringBuilder对象,否则可能产生问题。StringBuilder与StringBuffer的区别主要在于线程安全性方面,而且它不需要进行同步操作,因此在单线程环境中使用StringBuilder比StringBuffer效率更高。

七、wait()、sleep()、notify()、notifyAll()之间的区别

这四个方法都是Java中用于实现线程同步和互斥的方法,但它们之间有一些不同之处:

  1. wait():是Object类的方法,可以使线程等待直到另一个线程唤醒它。当线程调用wait()方法时,它会释放它所持有的对象锁,并进入等待状态,直到另一个线程调用相同对象上的notify()或notifyAll()方法唤醒它。

  2. sleep():是Thread类的一个静态方法,它用于使线程休眠一段时间,不会释放它所持有的对象锁。时间到后,线程会自动唤醒。

  3. notify():是Object类的方法,它用于唤醒一个正在等待的线程,让其进入就绪状态。但并不释放锁资源。

  4. notifyAll():是Object类的方法,它用于唤醒所有正在等待的线程。让它们进入就绪状态,但并不释放锁资源。

总之,wait()和notify()、notifyAl()都是用于线程之间的通信,同时也要涉及到锁机制。sleep()方法则是线程休眠,不会释放锁资源。wait()和sleep()都可以让线程处于暂停状态,但是wait()会释放占有的锁资源,而sleep()则不会。

八、线程的生命周期流程

线程的生命周期可以分为5个阶段:新建、就绪、运行、阻塞和死亡。

  1. 新建(New):当线程对象创建后,它将处于新建状态。此时它并没有开始运行,需要调用start()方法启动它。

  2. 就绪(Runnable):当调用start()方法之后,线程进入就绪状态。此时它已经准备好了被线程调度器调度,等待CPU的时间片资源。

  3. 运行(Running):当就绪状态的线程被线程调度器选中后,线程进入运行状态,开始执行run()方法的内容。

  4. 阻塞(Blocked):在某些情况下,当前线程可能会因为某种原因而阻塞,如等待同步锁或等待输入输出等。在这种情况下,线程进入阻塞状态,等待满足条件后返回就绪状态等待调度。

  5. 死亡(Terminated):当run()方法执行完毕后,或者调用stop()方法强制让线程停止时,线程进入死亡状态,生命周期结束。

九、==与equals方法的区别

在Java中,==与equals()方法都是用于比较两个对象是否相等,但它们之间有一些不同之处:

  1. ==:比较的是两个对象的引用是否相同,也就是比较这两个对象指向的内存地址是否相同,对于基本数据类型则比较的是它们的值是否相同。因此当比较的是两个对象时,如果它们的引用不同,则返回false,即使它们的内容是相同的。

  2. equals:比较的是两个对象的内容是否相同,也就是比较它们内部保存的数据是否相同。因此,对于不同的对象即使它们的引用不同,只要它们保存的数据相同,equals()方法将会返回true。

需要注意的是,默认情况下equals()方法是比较的两个对象的引用是否相等,这意味着如果没有重写equals()方法,则它与==的作用是一样的。如果想让equals()方法比较的是两个对象的内容是否相等,则应该在自定义类中重写该方法。

十、Java中字符串常量存放的区域

在Java中,字符串常量池是一块特殊的内存区域,它位于方法区或者堆区中。字符串常量池用于存储字符串常量,也就是被final关键字修饰且直接赋值的字符串。

在Java程序运行时,编译器会将所有的字符串常量都放到字符串常量池中。如果程序中有多个相同的字符串常量,那么它们在常量池中只会存在一份拷贝,这样就可以节省内存空间。

总之,字符串常量池是Java用于存储字符串常量的特殊内存区域,它可以避免重复创建相同的字符串,提高程序的效率和性能。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鱼粮爱编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值