语法基础
abstract抽象方法,—虚函数的实现原理;
byte的取值范围,— [-128, 127]
final关键字的作用,—修饰变量,修饰方法(不能被子类重写),修饰类(不能被继承)
单精度浮点数在初始化的时候,要加后缀f,否则默认为double类型
接口interface和抽象类abstract class 有什么区别?—接口是一种特殊的抽象类,接口中的所有方法默认都是抽象的,所有的字段都是静态常量。
总的来说,如果你想要定义一种契约,规定类应该提供哪些方法,那么应该使用接口。如果你想要提供一些通用的实现,并且希望子类可以选择性地覆盖这些实现,那么可以使用抽象类。
java函数中参数为对象时,其实是对象引用的值拷贝,可以通过.运算符来改变对象,但无法改变外部的对象引用的值。
1
2
3
4
5
6
7
8
9
10
11
12
13public 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的转换
使用Arrays.asList(array),但是转化为List后,不能增删,只能查改,否则抛异常。
1
2String[] 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方法使用ArrayList构造方法
1
2String[] array = {"apple", "banana", "orange"};
List<String> list = new ArrayList<>(Arrays.asList(array));此时,支持增删改查方法。但是效率不搞,适合List数据量不大的场景。
使用集合类Collections.addAll()方法
1
2
3String[] 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
3Arrays.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 {
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
22public 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,那么就是静态初始化块。静态初始化块在类加载时执行,只会执行一次,优先于实例初始化块的执行。