• java基础知识点总结篇一


    1、什么是面向对象?
    网上资料: 面向对象是一种基于面向过程的编程思想,是向现实世界模型的自然延伸,这是一种“万物皆对象”的编程思想。由执行者变为指挥者,在现实⽣生活中的任何物体都可以归一类事物,而一个体都是一类事物的实例。面向对象的编程是以对象为中心,以消息为驱动。
    我的回答: 一个实例的所有属性的综合,比如说一个学生,当创建一个学生的对象,那就需要学生的就读学校,就读班级,定位学生的学号,学生的学习成绩,学生的授课老师,这些属性综合起来就可以描述一个学生的群体,这就是一个对象,反正总的一句话,万事万物皆对象

    2、Java语言有哪些特点?
    答: 简单性、面向对象、分布性、编译和解释性、稳健性、安全性、可移植性、高性能、多线索性、动态性。

    3、JDK、JRE、JVM之间的区别
    答: JDK是 Java 语言的软件开发工具包,主要用于移动设备、嵌入式设备上的java应用程序。JDK是整个java开发的核心,它包含了JAVA的运行环境(JVM+Java系统类库)和JAVA工具。JRE可以让计算机系统运行Java应用程序(Java Application)。JVM是Java虚拟机的缩写,它是一种用于计算设备的规范,是一个虚构出来的计算机,它是通过在实际的计算机上仿真模拟各种计算机功能来实现的。
    Java语言的一个非常重要的特点就是与平台的无关性。而使用Java虚拟机是实现这一特点的关键。

    4、Java和C++的区别
    答: Java语言继承了C++语言的优点,去掉了C++中学习起来比较难的多继承、指针等概念,所以Java语言学习起来更简单,使用起来也更方便。

    5、什么是Java程序的主类?应用程序和小程序的主类有何不同?
    答: 一个程序中可以有多个类,但只能有一个类是主类。在 Java 应用程序中,这 个主类是指包含 main()方法的类。而在 Java 小程序中,这个主类是一个继承自系统类 JApplet 或 Applet 的子类。应用程序的主类不一定要求是 public 类,但小程序的主类要求必须是 public 类。主类是 Java 程序执行的入口点。

    6、构造器Constructor是否可被override
    答: 构造器是不能被继承的,因为每个类的类名都不相同,而构造器名称与类名相同,所以根本谈不上继承。又由于构造器不能继承,所以就不能被重写。但是,在同一个类中,构造器是可以被重载的。

    7、==和equals⽅法之前的区别
    答: 首先equals是一个方法,写在Object超类里面的方法,Object类中的equals方法默认比较地址值,但是一般的java工具类或者包装类中都重写了equals方法,比较内容是否相等。比如说:String、Date、File、包装类等都重写了Object类的equals方法,其中都是比较值相等。
    其次,== 是一个比较运算符,返回的就是一个布尔值true,false,如果比较的是基本数据类型变量,比较两个变量的值是否相等。(不一定数据类型相同),如果比较的是引用数据类型变量,比较两个对象的地址值是否相同,即两个引用是否指向同一个地址值

    		Integer i = 1234;
            Integer j = Integer.valueOf("1234");
            System.out.println(i == j); // false
            System.out.println(i.equals(j)); // true
    
            Integer a = 123;
            Integer b = 123;
            System.out.println(a == b);// true
            System.out.println(a.equals(b)); // true
            
            Integer c = 123;
            Integer d = new Integer("123");
            System.out.println(c == d); // false
            System.out.println(c.equals(d)); // true
            
     		Integer e = 123;
            Integer f = Integer.valueOf("123");
            System.out.println(e == f); // true
            System.out.println(e.equals(f)); // true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    8、hashCode()与equals()之间的关系
    答: hashCode()方法是java中的每一个对象都可以调用的方法,相当于一个独立的指纹信息,由对象导出的一个整型值,可以是负数,具有无规律性。可以利用hashCode来做一些提前的判断,如:、

    1、如果两个对象的hashCode不相同,那么这两个对象肯定是不同的对象。
    2、如果两个对象的hashCode相同,不代表这两个对象是同一个对象,也可能是两个对象
    3、如果两个对象相等,那么他们的hashCode就一定相同

    重写hashCode()方法后还需要重写equals方法, 首先是为了提高比较对象的效率,采取重写hashcode方法,先进行hashcode比较,如果不同,那么就没必要在进行equals的比较了,这样就大大减少了equals比较的次数,这对比需要比较的数量很大的效率提高是很明显的,其次,保证在equals相同的情况下hashcode值必定相同,如果重写了equals而未重写hashcode方法,可能就会出现两个没有关系的对象equals相同的(因为equal都是根据对象的特征进行重写的),但hashcode确实不相同的。
    两个方法重写后的联系:

    1、equals()相等的两个对象,hashcode()一定相等;
    2、hashcode()不等,一定能推出equals()也不等;
    3、hashcode()相等,equals()可能相等,也可能不等。
    4、所以先进行hashcode()判断,不等就不用equals()方法了。
    5、但equels是是根据对象的特征进行重写的,有时候特征相同,但hash值不同,也不是一个对象。 所以两个都重写才能保障是同一个对象。

    9、final关键字的作⽤是什么?
    答: final是一个修饰符(关键字)有三种用法:

    1、如果一个类使用了final修饰,那么这个类不可以再被其他类派生,即不可以被继承,可以理解为final与abstract为反义词。
    2、如果使用final修饰了变量,那么可以确保的是这个变量在使用的过程中不被改变,另外在声明的初始时候就必须要给变量初始化值。
    3、如果使用final修饰方法,这个方法不可以被派生类重写,只有使用权限。

    10、String、StringBuffer、StringBuilder的区别
    答: String类是不可变类,当一个String对象被创建,则包含在对象中的字符序列是不可改变的,直至对象被销毁;StringBuffer对象代表可变字符串对象,且线程安全;StringBuilder类代表可变字符串对象,且非线程安全

    11、重载和重写的区别
    答: 重载: 编译时多态、同一个类中同名的方法具有不同的参数列表、不能根据返回类型进行区分(函数调用时不能指定类型信息,编译器不知道要调用的是哪个函数);
    重写(覆盖): 运行时多态,子类与父类之间、子类重写父类的方法具有相同的返回类型、更好的访问权限。

    12、接口和抽象类的区别
    答: 接口(interface)可以说成是抽象类的一种特例,接口中的所有方法都必须是抽象的。 接口中的方法定义默认为public abstract类型,接口中的成员变量类型默认为public static final。另外,接口和抽象类在方法上有区别:

    1、抽象类可以有构造方法,接口中不能有构造方法。
    2、抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。
    3、抽象类中可以有普通成员变量,接口中没有普通成员变量。 4、抽象类中的抽象方法的访问类型可以是public,protected和默认类型。
    5、抽象类中可以包含静态方法,接口中不能包含静态方法。
    6、抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static
    final类型,并且默认即为public static final类型。
    7、一个类可以实现多个接口,但只能继承一个抽象类。二者在应用方面也有一定的区别:接口更多的是在系统架构设计方法发挥作用,主要用于定义模块之间的通信契约。而抽象类在代码实现方面发挥作用,可以实现代码的重用,例如,模板方法设计模式是抽象类的一个典型应用,假设某个项目的所有Servlet类都要用相同的方式进行权限判断、记录访问日志和处理异常,那么就可以定义一个抽象的基类,让所有的Servlet都继承这个抽象基类,在抽象基类的service方法中完成权限判断、记录访问日志和处理异常的代码,在各个子类中只是完成各自的业务逻辑代码。

    13、List和Set的区别
    答: List 集合代表一个元素有序、可重复的集合,集合中每个元素都有其对应的顺序索引 。 List 集合允许使用重复元素,可以通过索引来访问指定位置的集合元素 。 List 集合默认按元素的添加顺序设置元素的索引。Set集合只能存放无序的,不能重复的数据, Set集合与Collection的用法基本类似,可以这么说,Set集合就是Collection(但Set集合不能存放相同的元素,如果使用add添加相同的元素,add会返回false,且添加的元素也没有添加进去)

    14、ArrayList和LinkedList区别
    答: 1、数据结构不同 ArrayList是Array(动态数组)的数据结构,LinkedList是Link(链表)的数据结构。2、效率不同 当随机访问List(getset操作)时,ArrayList比LinkedList的效率更高,因为LinkedList是线性的数据存储方式,所以需要移动指针从前往后依次查找。当对数据进行增加和删除的操作(addremove操作)时,LinkedList比ArrayList的效率更高,因为ArrayList是数组,所以在其中进行增删操作时,会对操作点之后所有数据的下标索引造成影响,需要进行数据的移动。3、适应性不同,ArrayList自由性较低,因为它需要手动的设置固定大小的容量(如果不设置固定的容量大小,那初始化后会自动设置DEFAULT_CAPACITY = 10的大小,如果超过了这个容量大小,会自动扩展为原来的1.5倍),但是它的使用比较方便,只需要创建,然后添加数据,通过调用下标进行使用;而LinkedList自由性较高,能够动态的随数据量的变化而变化,但是它不便于使用。4、主要控件开销不同 ArrayList主要控件开销在于需要在List列表预留一定空间;而LinkList主要控件开销在于需要存储结点信息以及结点指针信息。

    15、HashMap和HashTable有什么区别?其底层实现是什么?
    答:
    相同点: hashmap和Hashtable都实现了map、Cloneable(可克隆)、Serializable(可序列化)这三个接口
    HashMap的底层原理:
    在这里插入图片描述
    两者的表面区别:
    1、HashMap是非线程安全的,HashTable是使用synchronized实现线程安全的。
    2、HashMap的键和值都允许有null值存在,而HashTable则不行。
    3、因为线程安全的问题,HashMap效率比HashTable的要高。
    4、Hashtable是同步的,而HashMap不是。因此,HashMap更适合于单线程环境,而Hashtable适合于多线程环境。

    两者的底层区别:

    1、HashMap的底层数据结构不同: jdk1.7底层都是数组+链表,但jdk1.8 HashMap加入了红黑树
    2、添加key-value的hash值算法不同:HashMap添加元素时,是使用自定义的哈希算法,而HashTable是直接采用key的hashCode()
    3、实现方式不同:Hashtable 继承的是 Dictionary类,而 HashMap 继承的是 AbstractMap 类。
    4、初始化容量不同:HashMap 的初始容量为:16,Hashtable 初始容量为:11,两者的负载因子默认都是:0.75。
    5、扩容机制不同:当已用容量>总容量 * 负载因子时,HashMap 扩容规则为当前容量翻倍,Hashtable 扩容规则为当前容量翻倍 +1。
    6、支持的遍历种类不同:HashMap只支持Iterator遍历,而HashTable支持Iterator和Enumeration两种方式遍历。
    7、迭代器不同:HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。而Hashtable则不会。
    8、部分API不同:HashMap不支持contains(Object value)方法,没有重写toString()方法,而HashTable支持contains(Object value)方法,而且重写了toString()方法
    9、同步性不同:Hashtable是同步(synchronized)的,适用于多线程环境,而hashmap不是同步的,适用于单线程环境。多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。

    最后,一般现在不建议用HashTable:

    ①是HashTable是遗留类,内部实现很多没优化和冗余。
    ②即使在多线程环境下,现在也有同步的ConcurrentHashMap替代,没有必要因为是多线程而用HashTable

    16、Jdk 7 到Jdk 8 HashMap 发生了什么变化(底层)?
    答: 1、hash源码不同
    jdk1.7的HashMap类的hash源码:

    static int hash(int h) {
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }
    
    • 1
    • 2
    • 3
    • 4

    jdk1.8的HashMap类的hash源码:

     static final int hash(Object key) {
          int h;
          // key.hashCode():返回散列值也就是hashcode
          // ^ :按位异或
          // >>>:无符号右移,忽略符号位,空位都以0补齐
          return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    整体来说,JDK7的HashMap类的hash方法实现的整体性能差,而JDK8实现的HashMap类的hash方法性能比较好,因为JDK7的HashMap类的hash方法扰动了四次。

    2、底层数据结构不同 java1.8之前的底层数据结构是数组加链表,数组是HashMap的主体,java1.8以后添加了红黑树解决哈希冲突的问题。
    3、链表插入方式的不同,在1.7之前,链表元素的插入采用的是头插法,也就是说,当有新结点进来时,会在插入在链表的头部,这种方法的效率非常高,不需要遍历链表,在java1.8以后,链表元素的插入采用的数尾插法,这种方式也解决了多线程下可能引发的死锁问题。因为头插法的链表在扩容移动时,会被逆序,即后插入的先被处理,如果这个时候有另一线程进行get操作,就有可能引发死锁。

    4、扩容机制不同 JDK7及其之前的java版本中HashMap的底层是链表加数组的结构,那么扩容的过程中,需要遍历链表上的每一个元素,然后按照每一个元素的hashCode值进行计算出新数组的下标。

    JDK8之后的HashMap类的底层数据结构是数组和链表加红黑树,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于64,那么会优先选择数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。在JDK8中会使用到一个双向链表来维护红黑树中的元素,首先jdk8在转移某个位置上的元素时,会判断这个位置是不是一个红黑树,如果这个位置是一个红黑树,那么会遍历该位置的双向链表,遍历双向链表统计哪些元素在扩容完之后还是原位置,哪些元素在扩容之后在新位置,这样遍历完双向链表后,就会得到两个子链表,一个放在原下标位置,一个放在新下标位置,如果原下标位置或新下标位置没有元素,则红黑树不用拆分,否则判断这两个子链表的长度,如果超过八,则转成红黑树放到对应的位置,否则把单向链表放到对应的位置。

    17、说⼀下HashMap的Put方法
    答: HashMap类添加元素首先需要根据hash值计算数组下标,如果该位置没有元素,那就直接插入,如果定位到的数组中有元素,那就需要和该元素比较hash值和key值,如果key值相等,那就直接覆盖,如果key值不相等,那就需要判断该节点是否为树节点或者是链表节点,如果是树节点,那就直接调用树节点的插入元素的方法e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value)将元素添加进入。如果是链表节点,那就需要遍历链表节点并比较是否是相同的元素,如果实现相同的元素,那就执行值覆盖,返回旧值,如果链表中不存在相同的元素,那就把添加进的元素插入链表的尾部。注意: 使用者只能使用HashMap提供的put()方法,而不能使用putVal()方法。putVal()方法只能内部使用。
    在这里插入图片描述
    源代码解析:

    // 将指定的值与此映射中的指定键相关联。如果映射先前包含键的映射,则替换旧值
    public V put(K key, V value) {
    		// 传入key的hash值,key值,value值
            return putVal(hash(key), key, value, false, true);
        }
    /**
     * @param key的hash值
     * @param key的值
     * @param 要存放的value值
     * @param onlyIfAbsent 如果为真,则不改变已存在的值。
     * @param evict 如果为 false,则表处于创建模式。
     */ 
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                       boolean evict) {
            Node<K,V>[] tab; Node<K,V> p; int n, i;
            // 判断table是否初始化完成,没有初始化完成则调用扩容函数
            if ((tab = table) == null || (n = tab.length) == 0)
                n = (tab = resize()).length;
            // (n - 1) & hash 确定元素存放在哪个桶中,桶为空,
            // 新生成结点放入桶中(此时,这个结点是放在数组中)
            if ((p = tab[i = (n - 1) & hash]) == null)
                tab[i] = newNode(hash, key, value, null);
             // 如果桶中已经存在了其他的元素,不为空
            else {
                Node<K,V> e; K k;
                // 分别比较hash值,key值是否相等而且不为空,如果符合,则取出这个节点
                if (p.hash == hash &&
                    ((k = p.key) == key || (key != null && key.equals(k))))
                    // 使用 e来存储 p
                    e = p;
                //如果hash值不相等,也就是key不相等;则判断节点是否为树节点
                else if (p instanceof TreeNode)
                // 调用红黑树插入值的方法将元素数据存放进树节点中。
                    e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
                // 上面的底层数据结构都不是,那就是链表的数据结构了。
                else {
                // 遍历到链表最后的节点
                    for (int binCount = 0; ; ++binCount) {
                    // 为空则是最后的节点。
                        if ((e = p.next) == null) {
                        // 在节点尾部插入数据
                            p.next = newNode(hash, key, value, null);
                             // 结点数量达到阈值(默认为 8 ),执行 treeifyBin 方法
                        	// 这个方法会根据 HashMap 数组来决定是否转换为红黑树。
                        	// 只有当数组长度大于或者等于 64 的情况下,才会执行转换红黑树操作,以减少搜索时间。否则,就是只能对数组扩容。
                            if (binCount >= TREEIFY_THRESHOLD - 1)
                                treeifyBin(tab, hash);
                            break;
                        }
                        // 判断新添加的元素是否存在于链表中,也就是判断hash值和key值是否相等且不为空。
                        if (e.hash == hash &&
                            ((k = e.key) == key || (key != null && key.equals(k))))
                            // 直接跳出,因为已经存在了该值。
                            break;
                        p = e;
                    }
                }
                // e!=null 说明存在旧值的key与要插入的key"相等"
                if (e != null) {
                    V oldValue = e.value;
                    // 进行值覆盖,然后返回旧值
                    if (!onlyIfAbsent || oldValue == null)
                        e.value = value;
                    afterNodeAccess(e);
                    return oldValue;
                }
            }
            ++modCount;
            // 如果是由于新添加的元素导致容量达到最大值,则执行扩容
            if (++size > threshold)
                resize();
            afterNodeInsertion(evict);
            return null;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    18、对象的相等与指向他们的引用相等,两者有什么不同?
    答: 对象的相等是指对象所存储的内容是否相等,引用相等是指引用所指向的地址是否相等。

    19、Java中的值传递和引用传递
    答: 值传递 : 方法接收的是实参值的拷贝,会创建副本。引用传递 : 方法接收的直接是实参所引用的对象在堆中的地址,不会创建副本,对形参的修改将影响到实参。在java语言中,只有值传递,没有引用传递,在java编程中,如果传入方法中的是基本的数据类型,那就会拷贝基本数据类型的变量(创建副本,值传递),如果传入方法的是引用类型,那就会拷贝引用类型的地址值(值传递)。

  • 相关阅读:
    创建一个简单的贪吃蛇游戏:HTML、CSS和JavaScript教程
    Keras深度学习实战(16)——自编码器详解
    智慧法院解决方案-最新全套文件
    ESP32主板-MoonESP32
    Dubbo 应用切换 ZooKeeper 注册中心实例,流量无损迁移
    让golang程序生成coredump文件并进行调试
    uniapp H5页面调用微信支付
    CenterPoint 源码流程解读(二)
    c++可变参数模板
    [附源码]计算机毕业设计面向高校活动聚AppSpringboot程序
  • 原文地址:https://blog.csdn.net/m0_46198325/article/details/126219987