Java Learning
Data Type
Primitive data type
Data Type | Size | Range |
byte | 1 byte | \(-128\) to \(127\) |
short | 2 bytes | \(-2^{16-1}\) to \(2^{16-1}-1\) |
int | 4 bytes | \(-2^{32-1}\) to \(2^{32-1}-1\) |
long | 8 bytes | \(-2^{64-1}\) to \(2^{64-1}-1\) |
float | 4 bytes | |
double | 8 bytes | |
boolean | 1 bit | |
char | 2 bytes |
int[] intArr = new int[3]; // 默认初始化为[0, 0, 0]
boolean[] boolArr = new boolean[3]; // 默认初始化为[false, false, false]
基本数据类型不能是null
。
十进制数转化为Java float
类型举例。
\(2.25=\color{green}{10.01}=\color{green}{1.001}\times \color{green}{2^1}\)
符号位:\(\color{green}{0}\)
指数位:\(127+1 = \color{green}{01111111}+\color{green}{1}=\color{green}{10000000}\)
尾数:\(\color{green}{001}\mathop{\rightarrow}^\text{补位}\color{green}{00100000000000000000000}\)
将符号位、指数为、尾数按顺序拼接在一起就是32位float
类型数据。
Array
Java语言原生支持。
声明时需要指定长度,长度固定,使用连续的内存空间。
可以存储基本数据类型(如
int
,char
)以及对象(如Integer
、String
)
代码样例:
import java.util.Arrays;
import java.util.List;
public class ArrayAnalysis {
public static void main(String[] args) {
// 声明Array
int[] intArray = {1, 2, 3, 3, 9, 5, 4, 8};
String[] stringArray = new String[10];
int[][] intArray2D = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
// 打印Array
System.out.println(intArray); // 打印出intArray的地址
System.out.println(Arrays.toString(intArray)); // 打印出inArray的内容
System.out.println(Arrays.deepToString(intArray2D)); // 打印出intArray2的内容
System.out.println(intArray[1]); // 打印出索引为1处的元素
// 排序Array
Arrays.sort(intArray); // 这行代码会直接修改intArray,返回值为void
// 浅拷贝Array
int[][] newIntArray2D = Arrays.copyOf(intArray2D, intArray2D.length); // 第二个参数的意思是截取Array中前intArray2.length个元素。这里是浅拷贝。
intArray2D[0][0] = -99;
System.out.println(Arrays.deepToString(newIntArray2D)); // [[-99, 2, 3], [4, 5, 6], [7, 8, 9]]
// 深拷贝Array
newIntArray2D = Arrays.stream(intArray2D).map(arr -> Arrays.copyOf(arr, arr.length)).toArray(int[][]::new);
intArray2D[0][0] = 99;
System.out.println(Arrays.deepToString(newIntArray2D)); // [[-99, 2, 3], [4, 5, 6], [7, 8, 9]]
// 将int[]转化为List<Integer>
List<Integer> myList = Arrays.stream(intArray).boxed().toList();
System.out.println(myList); // [1, 2, 3, 3, 4, 5, 8, 9]
}
}
List
ArrayList
和LinkedList
继承了List
,它们具备以下特点。
属于
java.util
包。ArrayList
具有动态扩容机制,当元素数量超过容量时会自动寻找新的连续内存空间并自动扩容1.5倍。LinkedList采用链表存储,便于元素的平频繁插入,但相比于ArrayList
,它的元素查找速度较慢为\(O(n)\)。仅能存储对象,不能存储基本数据类型。由于自动装箱机制,表面上看它们似乎也能直接存储基本数据类型。
具有丰富的方法可供调用。
另外,ArrayList
由于是“整块”,所以GC效率比LinkedList
更高。
代码样例:
import java.util.ArrayList;
import java.util.Arrays;
public class ArrayListAnalysis {
public static void main(String[] args) {
ArrayList<Integer> integerList = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6));
ArrayList<Integer> newIntegerList = integerList; // 这里是直接传递引用
System.out.println(integerList); // 由于ArrayList类重写了Object类中的toString()方法,所以这里直接打印出元素
// List的增删查改
integerList.set(0, 100);
integerList.add(99);
integerList.remove(0);
System.out.println(integerList.get(integerList.size() - 1));
System.out.println(newIntegerList);
// List<Integer>转化为基本数据类型int[]
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3));
int[] arr = list.stream().mapToInt(Integer::intValue).toArray();
// List<int[]>转化为基本数据类型int[][]
List<int[]> list2D = new ArrayList<>();
list2D.add(new int[] {1, 2, 3});
list2D.add(new int[] {4, 5, 6});
int[][] arr2D = list2D.toArray(new int[2][]); //或 list2D.toArray(int[][]::new)
}
}
LinkedList
和ArrayList
都实现了List
接口,因此拥有完全相同的方法。
ArrayList
占用的是连续空间,如果连续空间不足,则会新找一个足够的连续空间新创建一个ArrayList
并删除原有的ArrayList
。ArrayList
的优势在于获取数据的速度更快。
LinkedList
是链表的存储方式。LinkedList
的优势在于善于存储频繁被修改的数据。
String
- Immutability
String
对象一旦被创建就不能被改变,这也意味着任何String
实例方法的调用都不会改变实例本身。
- String Pool
用相同的字面量创建String
实例时,会直接返回池中的引用,不会创建新的对象,比如:
String str1 = "Hello";
String str2 = "Hello";
System.out.println(str1 == str2); // true
String str1 = new String("Hello");
String str2 = new String("Hello");
System.out.println(str1 == str2); // false
static methods
String.valueOf()
// 将基本数据类型转化为字符串
double num = 3.1415;
String numStr = String.valueOf(num);
// 将char[]类型转化为字符串
char[] charArr = {'A', 'B', 'C'};
String charArrString = String.valueOf(charArr); // ABC
String.format()
String.format()
用于创建格式化字符串。
格式说明符 | 描述 |
%s | 字符串 |
%d | 十进制整数 |
%o | 八进制整数 |
%x | 十六进制整数 |
%f | 浮点数 |
%c | 单个字符 |
%b | 布尔值 |
%n | 换行符 |
// 控制浮点数精度(四舍五入)
String.format("%.2f", 3.14559); // 3.15
// 对齐与填充。例如:包含Hello在内一共是10个字符,右对齐
String.format("|%10s|%-10s|", "Hello", "World"); // | Hello|World |
// 将十进制数8转化为八进制数10
String.format("%o", 8);
// 补0
String.format("补零: %07.2f", (float) 123); // 0123.00
String.join()
instance methods
.length()
Return the length of a String
.
.charAt(int index)
Return the char at the index position.
.indexOf(String str)
or.indexOf(char myChar)
Return the index of the String
or char
when
it appears at the first time. Return \(-1\) when String
or
char
is illegal.
.substring(int beginIndex, int endIndex)
Return a sub-string from the beginIndex position to the endIndex-1 position.
.replace(char oldChar, char newChar)
Return a new String
in which all of the oldChar
are replaced by newChar.
.toLowerCase()
.toUpperCase()
.toCharArray()
.hashCode()
计算一个字符串的Hash值,返回int
类型
StringBuilder
可变性:可追加、插入、删除、修改字符。
线程不安全:例如在两个线程中同时给一个
StringBuilder
追加元素会导致该StringBuilder
增加的元素总数小于两个线程中增加元素的总和。
StringBuilder
在单线程环境中性能较好,多线程环境中不安全。
StringBuffer
在单线程环境中性能较差,多线程环境中安全。因为它的所有方法都设置了synchronized
同步锁,当它被一个线程访问时,其他线程必须等待。
static methods
instance methods
.length()
返回长度。
.append()
追加字符串。
.insert(int index, String myStr)
插入字符串。
.delete(int beginIndex, int endIndex)
删除字符串,从beginIndex到endIndex-1。
.deleteChatAt(int index)
删除指定位置的字符。
.toString()
转化为String
对象。
Integer
static methods
Integer.parseInt()
// 将String转化为int
Integer.parseInt("13245");
// 将String按照其本身的进制转化为十进制int类型
Integer.parseInt("1000", 2); // 8
Integer.toString()
Transfer int
to String
.
Integer.toBinaryString(int num)
instance methods
HashSet
不同于Python的字典类型,HashSet的键可以是可变类型,例如int[]
,List<?>
。HashSet的键通过equals()
和hashCode()
来判断键的唯一性。
HashSet<List<Integer>>
元素都相同且顺序也相同的List
可以合并为同一个键。
// 情况1
Set<List<Integer>> set = new HashSet<>();
List<Integer> list1 = new ArrayList<>(Arrays.asList(1, 2));
List<Integer> list2 = new ArrayList<>(Arrays.asList(2, 1));
System.out.println(list1.equals(list2)); // false
System.out.println(list1.hashCode() == list2.hashCode()); // false
set.add(list1);
set.add(list2);
System.out.println(set); // [[1, 2], [2, 1]]
// 情况2
Set<List<Integer>> set = new HashSet<>();
List<Integer> list1 = new ArrayList<>(Arrays.asList(1, 2));
List<Integer> list2 = new ArrayList<>(Arrays.asList(1, 2));
System.out.println(list1.equals(list2)); // true
System.out.println(list1.hashCode() == list2.hashCode()); // true
set.add(list1);
set.add(list2);
System.out.println(set); // [[1, 2]]
Java Modifiers
Access Modifiers
For class,
Modifier | Description |
public | The class is accessible by any other class |
default | The class is only accessible by classes in the same package. |
For attribute, method, constructor,
Modifier | Description |
public | The code is accessible for all classes. |
protected | The code is accessible in the same package. And the code is accessible in the subclass. |
default | The code is only accessible in the same package. |
private | The code is only accessible within the declared class. |
Non-Access Modifier
For class,
Modifier | Description |
final | The class cannot be inherited by other classes. |
abstract | The class is used to be inherited. The class cannot be used to create objects. |
For attribute and method,
Modifier | Description |
final | The attribute or method will not point to another object. |
static | The attribute or method belongs to the class , rather than an object |
abstract | Only be used on methods in an abstract class. |
transient | |
synchronized | |
volatile |
interface
interface is a special abstract
class.
Every attribute in it is treated as public
,
static
and final
; Every method in it is
treated as public
and abstract
Functional interface can only contain one abstract method. For example:
And I can use Lambda expression to implement the abstract method in a functional interface. For example:
Common Used Class
Object
Java中所有类都默认继承自Object
类。Object
类中有两个重要的方法:public boolean equals()
和public int hashCode()
。
按照Java规范,equals()返回为true时,两个比较对象的hashCode()必须相同。因此在重写上述两个方式要注意这一点。
重写equals()
和hashCode()
的模板代码:
import java.util.Objects;
class Student {
private String name;
private String address;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(name, student.name) && Objects.equals(address, student.address);
}
@Override
public int hashCode() {
return Objects.hash(name, address); // 保证相同name和address的Student对象哈希码一致
}
}
Arrays
Arrays like String[]
or
int[]
is static array, the length of which is fixed.
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
String[] myArray = {"item1", "item2"};
System.out.println(myArray[0]); // Show the first element of myArray.
System.out.println(myArray); // Show the hash code of the memory address of an array object.
System.out.println(Arrays.toString(myArray)); // Show all of the items in String daya type of an Array.
System.out.println(myArray.length); // Show the length of a String[].
}
}
ArrayList and LinkedList
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList<String> myList = new ArrayList<String>();
myList.add("A");
myList.add("B");
myList.add("C");
System.out.println(myList.get(1));// "B"
myList.remove(0);
}
}
.remove()
method can remove an ArrayList’s item both by
index and by value. If the items in an ArrayList are int
data type, and I want to remove the item (for example \(5\)) by its value, then I should use
.remove(Integer.valueOf(5))
. If I use
.remove(5)
, the item with index of \(5\) will be removed.
ArrayList is based on dynamic array. LinkedList is based on linked structure.
Use ArrayList for storing and accessing data.
Use LinkedList to manipulate data.
HashMap
import java.util.HashMap;
public class Main {
public static void main(String[] args) {
HashMap<int, String> myHashTable = new HashMap<int, String>();
myHashTable.put(0, "item1");
System.out.println(myHashTable.get(0));
myHashTable.remove(0); // remove target item.
myHashTable.clear(); // remove all items.
myHashTable.keySet(); // java.util.HashMap$KeySet, used for loop through keys in a HashTable.
myHashTable.values(); // java.uitl.HashMap$Values, used for loop through values in a HashTable.
myHashTable.containsKey(0) // to check if a key in a HashTable.
}
}
HashMap
是非线程安全的。ConcurrentHashMap
是线程安全的。使用ConcurrentHashMap
的.put()方法可以保证线程安全(不过貌似更应该使用.compute()方法?)
HashSet
A HashSet is a collection of items where every item is unique.
import java.util.HashSet;
public class Main {
public static void main(String[] args) {
HashSet<String> myHashSet = new HashSet<String>();
myHashSet.add("item1");
myHashSet.add("item2");
myHashSet.contains("item3"); // check if "item3" in myHashSet.
myHashSet.remove("item2");
myHashSet.clear();
myHashSet.size();
// myHashSet1.euqals(myHashSet2) check if two HashSets have same items.
}
}
enum
enum
is a special class
.
Template
Here is a senior example: add annotations to every enum
value.
AggregateEnum.java
public enum AggregateEnum {
MOST("最多", "max"),
AVERAGE("平均", "avg");
private String aggregateCH;
private String aggregateEn;
AggregateEnum(String aggregateCH, String aggreagteEN) {
this.aggregateCH = aggregateCH;
this.aggreagteEN = aggreagteEN;
}
public String getAggregateCH() { return aggregateCH; }
}
Main.java
public class Main {
public static void main(String[] args) {
AggregateEnum mymost = AggregateEnum.MOST;
System.out.println(mymost.getAggregateCH());
System.out.println(mymost.getAggregateEN());
}
}
Define an enum
and print its annotations.
For example, the parameters of “最多” and “max” in MOST() will be passed to constructor directly.
Java Polymorphism
When instantiating a subclass, I can declare the variable type as its parent class’ name. But when doing this, I cannot use attributes and methods unique to the subclass.
public class PolymorphismExample {
public static void main(String[] args) {
Animal myAnimal = new Animal();
Animal myDog = new Dog();
Animal myCat = new Cat();
myAnimal.speak(); // Output: This animal speaks in its own way.
myDog.speak(); // Output: Dog barks.
myCat.speak(); // Output: Cat meows.
Animal[] animals = {myAnimal, myDog, myCat};
for (Animal animal : animals) {
animal.speak(); // Call the speak method on each type of animal
}
}
}
Thread
创建线程
继承Thread
public class ThreadAnalysis extends Thread {
public static int count = 0;
@Override
public void run() {
for(int i = 0; i < 4000; i++) {
count++;
}
}
public static void main(String[] args) throws InterruptedException {
ThreadAnalysis threadAnalysis1 = new ThreadAnalysis();
ThreadAnalysis threadAnalysis2 = new ThreadAnalysis();
threadAnalysis1.start();
threadAnalysis2.start();
threadAnalysis1.join();
threadAnalysis2.join();
System.out.println(ThreadAnalysis.count); // 打印初的结果可能小于8000
}
}
实现Runnable
public class RunnableAnalysis implements Runnable {
public static int count = 0;
@Override
public void run() {
for(int i = 0; i < 4000; i++) {
count++;
}
}
public static void main(String[] args) throws InterruptedException {
Runnable task = new RunnableAnalysis();
Thread thread1 = new Thread(task, "my task1");
Thread thread2 = new Thread(task, "my task2");
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(RunnableAnalysis.count); // 打印初的结果可能小于8000
}
}
实现Callable
前述的两个创建多线程的方法的run()
方法都是没有返回结果的。而通过实现Callable
接口可以获取线程的返回结果。
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class CallableAnalysis {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(5); // 创建一个最大包含五个线程的线程池
List<Future<String>> futures = new ArrayList<>();
for (int i = 0; i < 10; i++) {
final int taskId = i;
Callable<String> task = () -> {
return "Task Id of " + taskId;
};
futures.add(executor.submit(task));
}
// 获取多线程的返回结果
for (Future<String> future : futures) {
try {
System.out.println(future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
executor.shutdown();
}
}
堆和元空间
JVM进程由main()
函数启动,main()
函数作为JVM进程中的主线程,同时还有一些其他的线程(Signal
Dispatcher等)。方法区在JDK 8
之后叫元空间(MetaSpace)。
多个线程共用进程的堆和方法区。实例变量存储在堆中,静态变量(包括其他类的静态变量)存储在方法区中。
堆和元空间变量存储实例
public class ThreadAnalysis {
// 存储在元空间中
public static int staticCounter = 0;
// 存储在堆中
public int heapCounter = 0;
public static void main(String[] args) throws InterruptedException {
ThreadAnalysis threadAnalysis = new ThreadAnalysis();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5000; i++) {
staticCounter++;
threadAnalysis.heapCounter++;
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 5000; i++) {
staticCounter++;
threadAnalysis.heapCounter++;
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("staticCounter(方法区)最终值: " + staticCounter); // 可能小于10000
System.out.println("heapCounter(堆)最终值: " + threadAnalysis.heapCounter); // 可能小于10000
}
}
synchronized
锁线程安全
public class ThreadAnalysis {
// 存储在元空间中
public static int staticCounter = 0;
// 存储在堆中
public int heapCounter = 0;
public static void main(String[] args) throws InterruptedException {
ThreadAnalysis threadAnalysis = new ThreadAnalysis();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5000; i++) {
synchronized (ThreadAnalysis.class) { staticCounter++; };
synchronized (threadAnalysis) { threadAnalysis.heapCounter++; };
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 5000; i++) {
synchronized (ThreadAnalysis.class) { staticCounter++; };
synchronized (threadAnalysis) { threadAnalysis.heapCounter++; };
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("staticCounter(方法区)最终值: " + staticCounter); // 10000
System.out.println("heapCounter(堆)最终值: " + threadAnalysis.heapCounter); // 10000
}
}
AtomicInteger
线程安全
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadAnalysis {
// 使用AtomicInteger保证原子性
public static AtomicInteger staticCounter = new AtomicInteger(0);
public AtomicInteger heapCounter = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
ThreadAnalysis threadAnalysis = new ThreadAnalysis();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5000; i++) {
staticCounter.incrementAndGet();
threadAnalysis.heapCounter.incrementAndGet();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 5000; i++) {
staticCounter.incrementAndGet();
threadAnalysis.heapCounter.incrementAndGet();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("staticCounter(方法区)最终值: " + staticCounter.get()); // 10000
System.out.println("heapCounter(堆)最终值: " + threadAnalysis.heapCounter.get()); // 10000
}
}
- 利用
Runnable
创建子线程。
Runnable
的定义如下:
Runnable
创建子线程。
Runnable task = () -> {System.out.println("This is thread 1");};
Thread thread1 = new Thread(task, "My thread 1");
thread1.start(); // start the thread1
try {
thread1.join(); // stop the main thread and wait for thread1 to complete
} catch (InterruptedException e) {
e.printStackTrace();
} // 要使用try-catch结构或在其所在的方法添加throws InterruptedException
- 利用
Thread
创建子线程
volatile关键字
线程要想使用进程中的共享内存中的变量a=0时,一般先将其拷贝进自己本地线程内存,对其进行操作(a++)后再写回进程中的共享内存。在完成写回操作之前,其他线程看到的变量a始终是0。
volatile
关键字可以确保变量a一直保持可见性,即它的值一但被修改,进程共享内存中的变量a会立即刷新。上述过程通过内存屏障实现,使得线程表现得像是直接在操作进程主内存。
volatile
可以保证数据可见性,但不能保证数据原子性;synchronized
两者均可保证。
volatile
的使用样例:及时控制一个线程的启停:
public class VolatileExample {
private volatile boolean running = true;
public void start() {
new Thread(() -> {
while (running) {
// 执行任务
}
}).start();
}
public void stop() {
running = false;
}
public static void main(String[] args) throws InterruptedException {
VolatileExample example = new VolatileExample();
example.start();
Thread.sleep(1000);
example.stop(); // 1秒后停止线程
}
}
设计模式
“工厂方法”模式
工厂方法模式让子类决定要实例化的类是哪一个。
// FactoryMethodDemo.java
package com.example;
// Button
interface Button {
void paint();
}
// Windows Button
class WindowsButton implements Button {
public void paint() {
System.out.println("This is Windows button.");
}
}
// Mac Button
class MacButton implements Button {
public void paint() {
System.out.println("This is Mac button.");
}
}
// GUI Factory Button
interface GUIFactory {
Button createButton();
}
// Windows Factory
class WindowsFactory implements GUIFactory {
public Button createButton() {
return new WindowsButton();
}
}
// Mac Factory
class MacFactory implements GUIFactory {
public Button createButton() {
return new MacButton();
}
}
public class FactoryMethodDemo {
public static void main(String[] args) {
// I don't need to specify a button. The factory will decide which button it is.
GUIFactory factory1 = new WindowsFactory();
Button newButton1 = factory1.createButton();
newButton1.paint();
GUIFactory factory2 = new MacFactory();
Button newButton2 = factory2.createButton();
newButton2.paint();
}
}
“抽象工厂”模式
“工厂方法”模式:一个接口和多个实现类。 “抽象工厂”模式:多个接口和每个接口分别的多个实现类。
“建造者”模式
“建造者”设计模式用于创建复杂对象。尤其是当对象的属性很多,一部分属性必选,一部分属性可选。
public class User {
private final String firstName; // 必需
private final String lastName; // 必需
private final int age; // 可选
private final String phone; // 可选
private final String address; // 可选
private User(UserBuilder builder) {
this.firstName = builder.firstName;
this.lastName = builder.lastName;
this.age = builder.age;
this.phone = builder.phone;
this.address = builder.address;
}
// UserBuilder静态类
public static class UserBuilder {
private final String firstName;
private final String lastName;
private int age = 0;
private String phone = "";
private String address = "";
public UserBuilder(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public UserBuilder age(int age) {
this.age = age;
return this;
}
public UserBuilder phone(String phone) {
this.phone = phone;
return this;
}
public UserBuilder address(String address) {
this.address = address;
return this;
}
public User build() {
return new User(this);
}
}
public static void main(String[] args) {
User user = new User.UserBuilder("John", "Doe").age(30)
.phone("1234567890")
.address("Fake Address 1234")
.build();
}
}
再构造函数中传入一个自定义的Builder静态类。这个Build类中的构造函数可以定义User类的必选属性;而其他方法可用于接收User类的可选属性,并返回Builder类。最终定义一个build方法用于实例化User类。
“适配器”模式
如果有一个第三方类不是开发者要使用的目标接口的实现,那么可以设计一个Adapter类作为中间件,使得调用目标接口的方法的同时能调用第三方类的方法。
具体来讲,Adapter类要实现目标接口,然后再Adapter类中:1. 接收第三方类的实例作为属性;2. 实现接口的一个方法以扩展这个实例方法。
package com.example;
// 目标接口
interface Target {
void request();
}
// 第三方类(被适配者)
class ThirdPartyClass {
public void specifiedRequest() {
// 第三方类的具体实现
}
}
// 适配器类
class AdapterClass implements Target {
private ThirdPartyClass thirdPartyClass;
public AdapterClass(ThirdPartyClass thirdPartyClass) {
this.thirdPartyClass = thirdPartyClass;
}
@Override
public void request() {
// 调用被适配者的方法,此处可以添加一些额外的处理
thirdPartyClass.specifiedRequest();
}
}
public class AdapterClinet {
public static void main(String[] args) {
ThirdPartyClass thirdPartyClass = new ThirdPartyClass();
Target target = new AdapterClass(thirdPartyClass);
target.request();
}
}
Java基础知识
JVM, JDK, JRE, 字节码
JVM是运行Java字节码的虚拟机,可以让Java实现“一次编译,随处可运行”
https://oss.javaguide.cn/github/javaguide/java/basis/jdk-include-jre.png
.java -> .class -> 机器码
javac编译器负责Java源代码到字节码的转换.java -> .class
解释器或JIT负责将字节码转换为机器码。
位移运算符
二进制转化
将int
类型转化为二进制。
先计算原码,再计算反码(正数反码不变,负数除符号位其余位取反),最后计算补码(正数补码+0,负数在反码的基础上+1)
样例1:5的二进制
- 原码:00000000,00000000,00000000,00000101
- 反码:00000000,00000000,00000000,00000101
- 补码:00000000,00000000,00000000,00000101 (二进制转化结果)
样例2:-5的二进制
- 原码:10000000,00000000,00000000,00000101
- 反码:11111111,11111111,11111111,11111010
- 补码:11111111,11111111,11111111,11111011(二进制转化结果)
样例3:Interger.MIN_VALUE的二进制
\[\text{Integer.MIN_VALUE}=-2^{31}\] 由于除开符号位外的另外31位最大只能表示到\(2^{31}-1\),因此无法用原码表示Integer.MIN_VALUE。
Integer.MIN_VALUE是补码系统的特殊值,直接构造它的补码:10000000,00000000,00000000,00000000
因此在Java中\(\text{Integer.MIN_VALUE}\times2=\text{Integer.MIN_VALUE}<<1=0\)!
位移运算
位移运算是基于补码的位移。
\[5<<3=5\times2^3=40\] \[-5<<3=-5\times2^3=-40\]
\(<<\)为左移运算符,高位丢弃,低位补零。
\(>>\)为右移运算符,高位补符号位,低位丢弃。
\(>>>\)无符号右移,忽略符号位,空位以0补齐。
例:对int类型左移40位的处理过程为:40%32=8,即左移8位。
Java知识点记录
- 子类不能缩小父类的访问权限,不然有违多态性。例如父类的
public
方法不能被子类重写为private
方法。
Java常见问题
内存泄漏
内存泄漏是指不再被使用的对象未能被GC机制自动回收。常见的内存泄漏如下。