Java基础+集合

一、Java基础+集合

1、Java基础
  1. 面向过程和面向对象的区别

    • 面向过程:面向过程性能比面向对象高。因为类调用时需要实例化,开销较大,消耗系统资源,在单片机、嵌入式、Linux中需要使用面向过程。但是没有面向对象易维护、易复用、易扩展。
    • 面向对象:易维护、易复用、易扩展,因为有封装、多态、继承特性。
    • Java 性能差的主要原因并不是因为它是⾯向对象语⾔,⽽是 Java 是半编译语⾔,最终的执⾏代码并不是可以直接被 CPU 执⾏的⼆进制机械码。
  2. Java语言的特点

    • 面向对象(封装、继承、多态)

    • 平台无关性(Java虚拟机实现平台无关性)

    • 支持多线程(C++ 语⾔没有内置的多线程机制,因此必须调⽤操作系统的多线程功能来进⾏多

      线程程序设计,⽽ Java 语⾔却提供了多线程⽀持)

    • 支持网络编程

  3. jvm、jdk、jre的解答

    1)jvm: 运行Java字节码的虚拟机。
    	a.字节码:jvm可以理解的代码,.class文件。它不面向任何指定的处理器,只面向虚拟机。在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。一次编译,到处运行。
    	
    2)jdk和jre
    	a.jdk拥有jre的一切,他还有编译器javac和工具(javadoc和jdb)
    	b.jre是Java运行时环境。只是为了运⾏⼀下 Java 程序的话,那么你只需要安装 JRE 就可以了。
    
  4. Java和C++的区别

    • 都是⾯向对象的语⾔,都⽀持封装、继承和多态
    • Java 不提供指针来直接访问内存,程序内存更加安全
    • Java 的类是单继承的,C++ ⽀持多重继承;虽然 Java 的类不可以多继承,但是接⼝可以多继承。
    • Java 有⾃动内存管理机制,不需要程序员⼿动释放⽆⽤内存
  5. 构造器constructor是否可以被override

    • Constructor不能被重写(override),可以被重载overload。
  6. 重写: 是⼦类对⽗类的允许访问的⽅法的实现过程进⾏重新编写。

    1)返回值类型、方法名、参数列表必须相同。抛出的异常范围小于等于父类,访问修饰符大于等于父类。
    2)如果父类方法被修饰符为 private/final/static 则子类不能重写该方法,但是被 static 修饰的方法能被再次声明。
    3)构造方法无法被重写。
    
  7. Java面向对象的三大特性:封装、继承、多态、

    1)封装:
    	a.对象的属性私有化,提供⼀些可以被外界访问的属性的⽅法。
    2)继承:
    	a.使⽤已存在的类的定义作为基础建⽴新类的技术,新类的定义可以增加新的数据或新的功能
    	b.注意:⼦类拥有⽗类对象所有的属性和⽅法(包括私有属性和私有⽅法),但是⽗类中的私有属性和⽅法⼦类是⽆法访问,只是拥有。
    	c.⼦类可以拥有⾃⼰属性和⽅法,即⼦类可以对⽗类进⾏扩展。
    	d.⼦类可以⽤⾃⼰的⽅式实现⽗类的⽅法
    3)多态:指程序中定义的引⽤变量所指向的具体类型和通过该引⽤变量发出的⽅法调⽤在编程时并不确定,⽽是在程序运⾏期间才确定,即⼀个引⽤变量到底会指向哪个类的实例对象,该引⽤变量发出的⽅法调⽤到底是哪个类中实现的⽅法,必须在由程序运⾏期间才能决定。
    	a.实现多态的两种方法:继承和接口
    
  8. String、StringBuffer 和 StringBuilder 的区别是什么? String 为什么是不可变的?

    • 可变性:String 类中使⽤ final 关键字修饰字符数组来保存字符串,private final char value[] ,所以 String 对象是不可变的。⽽ StringBuilder 与 StringBuffer 都继承⾃ AbstractStringBuilder 类,在

      AbstractStringBuilder 中也是使⽤字符数组保存字符串 char[]value 但是没有⽤ final 关键字修饰,所以这两种对象都是可变的。

      AbstractStringBuilder.java
      //AbstractStringBuilder 是StringBuilder 与 StringBuffer 的公共⽗类,定义了⼀些字符串的基本操作,如 expandCapacity、append、insert、indexOf 等公共⽅法。    
      abstract class AbstractStringBuilder implements Appendable, CharSequence
      {
           /**
           * The value is used for character storage.
           */
           char[] value;
           /**
           * The count is the number of characters used.
           */
           int count;
           AbstractStringBuilder(int capacity) {
           value = new char[capacity];
       }
      
    • 线程安全性:

      1) String 中的对象是不可变的,也就可以理解为常量,线程安全
      2) StringBuffer 对⽅法加了同步锁或者对调⽤的⽅法加了同步
      锁,所以是线程安全的。
      3) StringBuilder 并没有对⽅法进⾏加同步锁,所以是⾮线程安全的
      
    • 性能:

      1)每次对 String 类型进⾏改变的时候,都会⽣成⼀个新的 String 对象,然后将指针指向新的 String 对象。
      2) StringBuffer 每次都会对 StringBuffer 对象本身进⾏操作,⽽不是⽣成新的对象并改变对象引⽤。   
      3) StringBuilder 相⽐使⽤ StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的⻛险    
      

      总结:

      • 操作少量的数据: 适⽤ String
      • 单线程操作字符串缓冲区下操作⼤量数据: 适⽤ StringBuilder
      • 多线程操作字符串缓冲区下操作⼤量数据: 适⽤ StringBuffer
  9. 自动装箱和自动拆箱

    装箱:  自动将基本数据类型转换为包装器类型;
    拆箱:  自动将包装器类型转换为基本数据类型。
    在装箱的时候自动调用的是Integer的valueOf(int)方法。而在拆箱的时候自动调用的是Integer的intValue方法。
        
    public class Main {
        public static void main(String[] args) {
             
            Integer i1 = 100;
            Integer i2 = 100;
            Integer i3 = 200;
            Integer i4 = 200;
             
            System.out.println(i1==i2);			//true
            System.out.println(i3==i4);			//false
        }
    }
    
    注意: 创建Integer对象的时候,如果数值在[-128,127]之间,便返回指向IntegerCache.cache中已经存在的对象的引用;否则创建一个新的Integer对象。
    
    public class Main {
        public static void main(String[] args) {
             
            Double i1 = 100.0;
            Double i2 = 100.0;
            Double i3 = 200.0;
            Double i4 = 200.0;
             
            System.out.println(i1==i2);			// false
            System.out.println(i3==i4);			// false
        }
    }
    
    注意 : Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的。Double、Float的valueOf方法的实现是类似的。
    
    public class Main {
        public static void main(String[] args) {
             
            Boolean i1 = false;
            Boolean i2 = false;
            Boolean i3 = true;
            Boolean i4 = true;
             	
            System.out.println(i1==i2);			// true
            System.out.println(i3==i4);			// true
        }
    }
    
    Boolean中定义了2个静态成员属性:
    
    public class Main {
        public static void main(String[] args) {
             
            Integer a = 1;
            Integer b = 2;
            Integer c = 3;
            Integer d = 3;
            Integer e = 321;
            Integer f = 321;
            Long g = 3L;
            Long h = 2L;
             	
            System.out.println(c==d);				// true
            System.out.println(e==f);				// false
            System.out.println(c==(a+b));			// true
            System.out.println(c.equals(a+b));		// true
            System.out.println(g==(a+b));			// true
            System.out.println(g.equals(a+b));		// false
            System.out.println(g.equals(a+h));		// true
        }
    }
    
    a."=="运算符的两个操作数都是包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。另外,对于包装器类型,equals 方法并不会进行类型转换.
        
        
    b.第一个和第二个输出结果没有什么疑问。
      第三句由于 a+b 包含了算术运算,因此会触发自动拆箱过程(会调用intValue方法),因此它们比较的是数值是否相等。
      而对于c.equals(a+b)会先触发自动拆箱过程,再触发自动装箱过程,也就是说a+b,会先各自调用intValue方法,得到了加法运算后的数值之后,便调用Integer.valueOf方法,再进行equals比较。
      同理对于后面的也是这样,不过要注意倒数第二个和最后一个输出的结果(如果数值是int类型的,装箱过程调用的是Integer.valueOf;如果是long类型的,装箱调用的Long.valueOf方法)。    
    
  10. 在⼀个静态⽅法内调⽤⼀个⾮静态成员为什么是⾮法的?

    • 由于静态⽅法可以不通过对象进⾏调⽤,因此在静态⽅法⾥,不能调⽤其他⾮静态变量,也不可以访问⾮静态变量成员。
  11. 在 Java 中定义⼀个不做事且没有参数的构造⽅法的作⽤

    • 1)Java程序在执⾏⼦类的构造⽅法之前,如果没有⽤ super() 来调⽤⽗类特定的构造⽅法,则会调⽤⽗类中“没有参数的构造⽅法”。
      2)因此,如果⽗类中只定义了有参数的构造⽅法,⽽在⼦类的构造⽅法中
      ⼜没有⽤ super() 来调⽤⽗类中特定的构造⽅法,则编译时将发⽣错误,因为 Java 程序在⽗类中找不到没有参数的构造⽅法可供执⾏。解决办法是在⽗类⾥加上⼀个不做事且没有参数的构造⽅法。
      
  12. 接口和抽象类的区别

    1)接⼝的⽅法默认是 public,所有⽅法在接⼝中不能有实现(Java 8 开始接⼝⽅法可以有默认实现),⽽抽象类可以有⾮抽象的⽅法。
    2)接⼝中除了 static、final 变量,不能有其他变量,⽽抽象类中则不⼀定。
    3)⼀个类可以实现多个接⼝,但只能实现⼀个抽象类。接⼝⾃⼰本身可以通过 extends 关键字扩展多个接⼝。
    4)接⼝⽅法默认修饰符是 public,抽象⽅法可以有 public、protected 和 default 这些修饰符(抽象⽅法就是为了被重写所以不能使⽤ private 关键字修饰!)。
    
  13. 成员变量

    1.从语法形式上看:成员变量是属于类的,⽽局部变量是在⽅法中定义的变量或是⽅法的参数;成员变量可以被 public,private,static 等修饰符所修饰,⽽局部变量不能被访问控制修饰符及static 所修饰;但是,成员变量和局部变量都能被 final 所修饰。
    2.从变量在内存中的存储⽅式来看:如果成员变量是使⽤ static 修饰的,那么这个成员变量是属于类的,如果没有使⽤ static 修饰,这个成员变量是属于实例的。对象存于堆内存,如果局部变量类型为基本数据类型,那么存储在栈内存,如果为引⽤数据类型,那存放的是指向堆内存对象的引⽤或者是指向常量池中的地址。
    3.从变量在内存中的⽣存时间上看:成员变量是对象的⼀部分,它随着对象的创建⽽存在,⽽局部变量随着⽅法的调⽤⽽⾃动消失
    4.
    
  14. 构造方法有那些特性

    1. 名字与类名相同。
    2. 没有返回值,但不能⽤ void 声明构造函数。
    3. ⽣成类的对象时⾃动执⾏,⽆需调⽤。
    
  15. 静态⽅法和实例⽅法有何不同

    1. 在外部调⽤静态⽅法时,可以使⽤"类名.⽅法名"的⽅式,也可以使⽤"对象名.⽅法名"的⽅式。
    ⽽实例⽅法只有后⾯这种⽅式。也就是说,调⽤静态⽅法可以⽆需创建对象。
    2. 静态⽅法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态⽅法),⽽不允许访问实例成员变量和实例⽅法;实例⽅法则⽆此限制。
    
  16. == 与 equals(重要)

    1)、== : 它的作⽤是判断两个对象的地址是不是相等。即,判断两个对象是不是同⼀个对象(基本数据类型==比较的是值,引⽤数据类型==比较的是内存地址)2)、equals() : 它的作⽤也是判断两个对象是否相等。但⼀般有两种使⽤情:
    		情况 1:类没有覆盖 equals() ⽅法。则通过 equals()比较该类的两个对象时,等价于通过“==”比较这两个对象。
    		情况 2:类覆盖了 equals() ⽅法。⼀般,我们都覆盖 equals() ⽅法来比较两个对象的内容是否相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。
    		
    例子:
     public class test1 {
         public static void main(String[] args) {
             String a = new String("ab"); // a 为⼀个引⽤
             String b = new String("ab"); // b为另⼀个引⽤,对象的内容⼀样
             String aa = "ab"; // 放在常量池中
             String bb = "ab"; // 从常量池中查找
             if (aa == bb) // true
             System.out.println("aa==bb");
             if (a == b) // false,⾮同⼀对象
             System.out.println("a==b");
             if (a.equals(b)) // true
             System.out.println("aEQb");
             if (42 == 42.0) { // true
             System.out.println("true");
             }
     }
    }
    
    说明:
    1)String 中的 equals ⽅法是被重写过的,因为 object 的 equals ⽅法是⽐᫾的对象的内存地址,⽽ String 的 equals ⽅法比较的是对象的值。
    2)当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引⽤。如果没有就在常量池中重新创建⼀个 String 对象。
    
  17. hashCode 与 equals (重要)

    1hashCode() 的作⽤是获取哈希码,也称为散列码;它实际上是返回⼀个 int 整数。这个哈希码的作⽤是确定该对象在哈希表中的索引位置。
    2)为什么要有 hashCode?   
        先以“HashSet 如何检查重复”为例⼦来说明为什么要有 hashCode: 当你把对象加⼊ HashSet时,HashSet 会先计算对象的 hashcode 值来判断对象加⼊的位置,同时也会与该位置其他已经加⼊的对象的 hashcode 值作⽐᫾,如果没有相符的 hashcode,HashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调⽤ equals() ⽅法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加⼊操作成功。如果不同的话,就会重新散列到其他位置。
    
  18. 获取键盘输入常用的两种方法:

    方法1:通过Scanner
        Scanner input = new Scanner(System.in);
        String s = input.nextLine();
        input.close();
    
    方法2:通过BufferedReader
        BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
    	String s = input.readLine();
    
2、Java集合
  1. List、Set、Map的区别

    • List: List接⼝存储⼀组不唯⼀(可以有多个元素引⽤相同的对象),有序的对象
    • Set(注重独⼀⽆⼆的性质): 不允许重复的集合。不会有多个元素引⽤相同的对象。
    • Map(Key来搜索的专家**)😗* 使⽤键值对存储。Map会维护与Key有关联的值。两个Key可以引⽤相同的对象,但Key不能重复,典型的Key是String类型,但也可以是任何对象。
  2. Arraylist 与 LinkedList 区别?

    1)是否保证线程安全:ArrayList和LinkedList都是不同步的,也就是不保证线程安全;
    2)底层数据结构: Arraylist底层使⽤的是Object数组;LinkedList底层使⽤的是双向链表.
    3)插⼊和删除是否受元素位置的影响:
    	① ArrayList 采⽤数组存储,所以插⼊和删除元素的时间复杂度受元素位置的影响。⽐如:执⾏ add(E e) ⽅法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是O(1)。但是如果要在指定位置 i插⼊和删除元素的话( add(int index, E element) )时间复杂度就为(n-i)。因为在进⾏上述操作的时候集合中第i和第i个元素之后的(n-i)个元素都要执⾏向后位/向前移⼀位的操作。② LinkedList 采⽤链表存储,所以对于 add(​E e) ⽅法的插⼊,删除元素时间复杂度不受元素位置的影响,近似 O(1),如果是要在指定位置 i 插⼊和删除元素的话( (add(int index, E element) ) 时间复杂度近似为 o(n)) 因为需要先移动到指定位置再插⼊。
    
    4)是否支持快递访问:
    	LinkedList 不⽀持⾼效的随机元素访问,⽽ ArrayList⽀持。快速随机访问就是通过元素的序号快速获取元素对象(对应于 get(int index) ⽅法)。
    	
    RandomAccess接⼝:什么也没有实现,标识实现这个接⼝的类具有随机访问功能 	
    

    下面总结list的遍历方式选择

    • 实现了 RandomAccess 接⼝的list,优先选择普通 for 循环 ,其次 foreach
    • 未实现 RandomAccess 接⼝的list,优先选择iterator遍历(foreach遍历底层也是通过iterator实现的,),⼤size的数据,千万不要使⽤普通for循环
  3. ArrayList 与 Vector区别?为什么要⽤Arraylist取代Vector呢?

    a.Vector 类的所有⽅法都是同步的。可以由两个线程安全地访问⼀个Vector对象、但是⼀个线程访问Vector的话代码要在同步操作上耗费⼤量的时间。
    b.Arraylist 不是同步的,所以在不需要保证线程安全时建议使⽤Arraylist。
    
  4. HashMap 和 HashTable的区别

    1)线程是否安全:HashMap是线程非安全的,HashTable是线程安全的;HashTable 内部的⽅法基本都经过 synchronized 修饰。(如果你要保证线程安全的话就使⽤ ConcurrentHashMap吧!);
    2)效率: 因为线程安全的问题,HashMap 要⽐ HashTable 效率⾼⼀点。另外,HashTable 基本被·淘汰,不要在代码中使⽤它;
    3)底层数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了᫾⼤的变化,当链表⻓度⼤于阈值(默认为8)时,将链表转化为红⿊树,以减少搜索时间。Hashtable 没有这样的机制。
    
    
  5. HashMap 和 HashSet的区别

    如果你看过 HashSet 源码的话就应该知道:HashSet 底层就是基于 HashMap 实现的。
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z0PFK9Ri-1606391915214)(C:\Users\cfb\AppData\Roaming\Typora\typora-user-images\image-20201126164733150.png)]

  6. 集合框架底层数据结构总结

    Collection
    1. List
        ** Arraylist: Object数组
        ** Vector: Object数组
        ** LinkedList: 双向链表(JDK1.6之前为循环链表,JDK1.7取消了循环)
    2. Set
    	** HashSet(⽆序,唯⼀:基于 HashMap 实现的,底层采⽤HashMap 来保存元素
    	** LinkedHashSet:LinkedHashSet 继承于HashSet,并且其内部是通过 LinkedHashMap 来实现的。有点类似于我们之前说的LinkedHashMap 其内部是基于 HashMap 实现⼀样,不过还是有⼀点点区别的
    	** TreeSet(有序,唯⼀): 红⿊树(⾃平衡的排序⼆叉树)    
    	
    3、Map
    	** HashMap:JDK1.8之前HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突⽽存在的(“拉链法”解决冲突)。JDK1.8以后在解决哈希冲突时有了᫾⼤的变化,当链表⻓度⼤于阈值(默认为8)时,将链表转化为红⿊树,以减少搜索时间
    	** LinkedHashMap: LinkedHashMap 继承⾃ HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红⿊树组成。另外,LinkedHashMap 在上⾯结构的基础上,增加了⼀条双向链表,使得上⾯的结构可以保持键值对的插⼊顺序。同时通过对链表进⾏相应的操作,实现了访问顺序相关逻辑。
    	** Hashtable: 数组+链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突⽽存
    在的
    	** TreeMap: 红⿊树(⾃平衡的排序⼆叉树)	
    
  7. 如何选择集合

    • ​ 主要根据集合的特点来选⽤,⽐如我们需要根据键值获取到元素值时就选⽤Map接⼝下的集合,需要排序时选择TreeMap,不需要排序时就选择HashMap,需要保证线程安全就选⽤ConcurrentHashMap.当我们只需要存放元素值时,就选择实现Collection接⼝的集合,需要保证元素唯⼀时选择实现Set接⼝的集合⽐如TreeSet或HashSet,不需要就选择实现List接⼝的⽐如ArrayList或LinkedList,然后再根据实现这些接⼝的集合的特点来选⽤。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值