语法基础

  • abstract抽象方法,—虚函数的实现原理;

  • byte的取值范围,— [-128, 127]

  • final关键字的作用,—修饰变量,修饰方法(不能被子类重写),修饰类(不能被继承)

  • 单精度浮点数在初始化的时候,要加后缀f,否则默认为double类型

  • 接口interface和抽象类abstract class 有什么区别?—接口是一种特殊的抽象类,接口中的所有方法默认都是抽象的,所有的字段都是静态常量。

    总的来说,如果你想要定义一种契约,规定类应该提供哪些方法,那么应该使用接口。如果你想要提供一些通用的实现,并且希望子类可以选择性地覆盖这些实现,那么可以使用抽象类。

  • java函数中参数为对象时,其实是对象引用的值拷贝,可以通过.运算符来改变对象,但无法改变外部的对象引用的值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class Main {
    public static void changeName(Person person) {
    //person = new Person("John Doe"); // 改变不了
    person.name = "John Doe";
    }

    public static void main(String[] args) {
    Person person = new Person("Jane Doe");
    System.out.println("Before: " + person.name);
    changeName(person);
    System.out.println("After: " + person.name);
    }
    }

数组与字符串

  • ArrayList底层也是使用的数组来实现的。transient Object[] elementData;

  • 数组与List的转换

    1. 使用Arrays.asList(array),但是转化为List后,不能增删,只能查改,否则抛异常。

      1
      2
      String[] array = {"apple", "banana", "orange"};
      List<String> list = Arrays.asList(array);

      若增删,异常为:java.lang.UnsupportedOperationException

      其原因在于,Arrays.asList方法返回java.util.Arrays类中一个私有静态内部类java.util.Arrays.ArrayList,它并不是java.util.ArrayList类,并不支持添加add和删除remove方法

    2. 使用ArrayList构造方法

      1
      2
      String[] array = {"apple", "banana", "orange"};
      List<String> list = new ArrayList<>(Arrays.asList(array));

      此时,支持增删改查方法。但是效率不搞,适合List数据量不大的场景。

    3. 使用集合类Collections.addAll()方法

      1
      2
      3
      String[] array = {"apple", "banana", "orange"};
      List<String> list = new ArrayList<>();
      Collections.addAll(list, array);

      相较于Arrays.addAll()更高效,其原因在于:避免了数组形式的转换(java - Why is Collections.addAll supposed to be faster than c.addAll - Stack Overflow

  • Arrays类的常用方法,参考JDK文档(Arrays (Java SE 11 & JDK 11 ) (oracle.com)

  • 打印Java数组

    使用Stream打印,三种形式

    1
    2
    3
    Arrays.asList(array).stream().forEach(s -> System.out.println(s));
    Stream.of(array).forEach(System.out::println);
    Arrays.stream(array).forEach(System.out::println);

    使用Arrays.toString(),可以将任意数组转化为String数组;Arrays.deepToString()可以打印二维数组。

  • String类的定义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // Java 9 之前
    public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    private final char value[];
    }
    // Java 9 之后:使用byte[]代替char[],在Latin-1字符编码下节省一半内存,但是需要额外的编码检测coder
    public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    @Stable
    private final byte[] value;
    private final byte coder;
    private int hash;
    }

    1)String类是final的,意味着它不能被子类继承;

    2)String类实现了Serializable接口,意味着它可以序列化

    3)String类实现了Comparable接口,意味着最好不要用“==”来比较两个字符串是否相等,而应该使用compareTo()方法去比较。

    4)StringBuffer、StringBuilder和String都实现了CharSequence接口。但是String是不可变的(因为value[]成员也被final修饰),另外两是可变的;

    5)Java 9之后使用byte[]作为底层实现后,一个中文用UTF16编码(2字节),一个英文用Latin-1编码(1字节)。

  • String类常用方法:截取子串substring(), 找子字符串第一次出现的位置indexOf(), 返回字符串长度length(), 判空isEmpty(), 获得指定编码字节数组getBytes(), 分割字符串split(), 比较两个字符串equals等。

  • 字符串常量池:由于字符串使用太频繁,故提供常量池提高性能

    对于这一行 String s = new String("aaa"); 会产生几个String对象?

    –> new关键字总会在堆上创建对象。如果字符串常量池中有,则在堆中创建一个对象,返回其地址到栈上的s;如果字符串常量池没有,则会创建两个对象,常量池中一个,堆中一个,并返回堆中对象的引用。Note: 先找常量池。

    String s = "bbb"; 这一行。如果常量池中存在则直接返回地址;若不存在则在常量池中创建一个返回引用。

  • String.ntern()方法可以主动地将堆中的字符串放入到常量池中。

  • 在循环体内拼接字符串,最好在外部建一个StringBuilder对象,内部使用其append()方法,而不是+号操作符。因为使用+号操作符会产生大量StringBuilder对象,占用更多内存空间,且java虚拟机会频繁地垃圾回收。

  • 拆分字符串,String.split(delimiter, parts),以delimiter为分隔符,分成几个部分,返回String数组。

面向对象编程

  • 访问权限修饰符:对于类,存在public和包级访问权限;对于成员变量和方法,存在默认的包级,以及private,protected,public这几种访问权限修饰符。

  • 代码初始化块:在构造方法执行的时候,编译器会把代码初始化块放到构造方法的最前面执行(但实际上也是在构造方法内),例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class Student {
    String name; Double math; Double chinese;

    Student(){}
    Student(String name, Double math, Double chinese) {
    this.name = name;
    this.math = math;
    this.chinese = chinese;
    System.out.println("Student含参构造函数");
    }

    {
    System.out.println("代码初始化块"); // 也称“实例初始化块”
    }

    public static void main(String[] args) {
    Student bb = new Student("aaa", 99.2, 56.7);
    }
    }
    -------------------output:
    >> 代码初始化块
    >> Student含参构造函数

    另外,如果代码初始化块前加了static,那么就是静态初始化块。静态初始化块在类加载时执行,只会执行一次,优先于实例初始化块的执行。