@CLSChen
2019-09-22T11:34:02.000000Z
字数 24793
阅读 1358
Java
编程
Java泛型详解:https://www.cnblogs.com/coprince/p/8603492.html
<>
,然后在调用它的时候用具体的类型来替换<>
里的内容。
Generic<T> //泛型类
T //泛型类型参数
List arrayList = new ArrayList();
arrayList.add("aaaa");
arrayList.add(100);
for(int i = 0; i< arrayList.size();i++){
String item = (String)arrayList.get(i);
Log.d("泛型测试","item = " + item);
}
// 运行结果
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
在运行的时候检测出来,不可以将Integer类转换成String,报错。
List arrayList<String>= new ArrayList<>();
arrayList.add("aaaa");
arrayList.add(100);
// 编辑器报错,不可以向其中添加Integer。
int
是不可以的。 Integer
。Java会自动将5
包装为new Integer(5)
,这个过程称为自动装箱。
ArrayList<Integer> intList = new ArratList<>();
intList.add(100);// 可以直接用100,而不用new Integer(100)
JDK1.5
之前没有使用泛型的时候,输出列表内的元素需要强制类型转换,因为当时的ArrayList
不知道其中存储的是什么类型的对象。
// 未给ArrayList声明对象类型
ArrayList dates = new ArrayList();
dates.add(new Date());
// 因此要进行强制类型转换
Date date1 = (Date)dates.get(0);
JDK1.5
之后,ArrayList
知道自己储存的是Date
对象。
// 声明ArrayList泛型的对象为Date,ArrayList知道自己储存的是Date对象。
ArrayList<Date> dates = new ArrayList<>();
dates.add(new Date());
// 可以直接读取
Date date1 = dates.get(0);
但是,当编译结束后,Java处理器仍然将泛型转化为普通类型(类型消除),并在最后加一个强制类型转换。也就是说,最后都会回到第一种情况(笑)。泛型只在编译的时候有效果,不会进入到运行时段。
因此,泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。因此对其使用instanceof是错误的。我们创建两个泛型列表。
ArrayList<String> list1 = new ArrayList<>;
ArrayList<Integer> list2 = new ArrayList<>;
ArrayList<String>
和ArrayList<Integer>
是两种类型,但是,运行时只有一个ArrayList
类会被加载到JVM
中。list1
和list2
都是ArrayList
类的实例,因此,下面两条语句的执行结果都为true
:
list1 instanceof ArrayList;
list2 instanceof ArrayList;
list1 instanceof ArrayList<String>;
// 不能使用[泛型类型参数]创建对象和数组
E test = new E();
E[] testarray = new E[5];
// 可以使用强制类型转换配合object类来做,但是仍然会有警告。
E[] testarray = (E[])new object[5];
---
// 使用[泛型类]创建对象是可以的
ArrayList<String> test = new ArrayList<String>;
// 但是使用[泛型类]创建数组也是不允许的
ArrayList<String>[] testarray = new ArrayList<String>[];
// 同样也可以使用强制类型转换,但是也会有警告
ArrayList<String>[] testarray = (ArrayList<String>[])new ArrayList<String>[];
---
// 静态方法和异常类不能是泛型的。
Integer
是Number
的子类,但是ArrayList<Integer>
不是ArrayList<Number>
的子类,虽然同一种泛型可以对应多种类型,这几种类型可能还会有些子类父类的关系,但是不同的泛型类实例是不兼容的。<T>,而创建类实例时需要指定对象类型如<String>,但是构造方法并不需要加
,构造方法保持原样。
// 泛型类定义
class Generic<T>{
// 构造方法
public Generic();
}
// 创造类实例
new Generic<String>
<T>
中的T),在很多方法中都会用到T,但是不是用到T的方法就是泛型方法,泛型方法有自己的定义。public
和返回值之间加了个<T>
,只有这样单独声明的方法才是泛型方法。泛型方法的泛型不收泛型类限制,可以向里面传入任何类型,而不只是类创建时声明的那个类型,泛型方法和泛型类是独立的。
public static <T> void print(E[] list){};
public class GenericTest {
//这个类是个泛型类,在上面已经介绍过
public class Generic<T>{
private T key;
public Generic(T key) {
this.key = key;
}
//我想说的其实是这个,虽然在方法中使用了泛型,但是这并不是一个泛型方法。
//这只是类中一个普通的成员方法,只不过他的返回值是在声明泛型类已经声明过的泛型。
//所以在这个方法中才可以继续使用 T 这个泛型。
public T getKey(){
return key;
}
/**
* 这才是一个真正的泛型方法。
* 首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T
* 这个T可以出现在这个泛型方法的任意位置.
* 泛型的数量也可以为任意多个
* 如:public <T,K> K showKeyName(Generic<T> container){
* ...
* }
*/
public <T> T showKeyName(Generic<T> container){
System.out.println("container key :" + container.getKey());
T test = container.getKey();
return test;
}
/**
* 这个方法是有问题的,编译器会为我们提示错误信息:"UnKnown class 'E' "
* 虽然我们声明了<T>,也表明了这是一个可以处理泛型的类型的泛型方法。
* 但是只声明了泛型类型T,并未声明泛型类型E,因此编译器并不知道该如何处理E这个类型。*/
public <T> T showKeyName(Generic<E> container){
...
}
/**
* 这个方法也是有问题的,编译器会为我们提示错误信息:"UnKnown class 'T' "
* 对于编译器来说T这个类型并未项目中声明过,因此编译也不知道该如何编译这个类。
* 所以这也不是一个正确的泛型方法声明。*/
public void showkey(T genericObj){
}
}
public class GenericFruit {
class Fruit{
@Override
public String toString() {
return "fruit";
}
}
class Apple extends Fruit{
@Override
public String toString() {
return "apple";
}
}
class Person{
@Override
public String toString() {
return "Person";
}
}
class GenerateTest<T>{
public void show_1(T t){
System.out.println(t.toString());
}
//在泛型类中声明了一个泛型方法,使用泛型E,这种泛型E可以为任意类型。可以类型与T相同,也可以不同。
//由于泛型方法在声明的时候会声明泛型<E>,因此即使在泛型类中并未声明泛型,编译器也能够正确识别泛型方法中识别的泛型。
public <E> void show_3(E t){
System.out.println(t.toString());
}
//在泛型类中声明了一个泛型方法,使用泛型T,注意这个T是一种全新的类型,可以与泛型类中声明的T不是同一种类型。
public <T> void show_2(T t){
System.out.println(t.toString());
}
}
public static void main(String[] args) {
Apple apple = new Apple();
Person person = new Person();
GenerateTest<Fruit> generateTest = new GenerateTest<Fruit>();
//apple是Fruit的子类,所以这里可以
generateTest.show_1(apple);
//编译器会报错,因为泛型类型实参指定的是Fruit,而传入的实参类是Person
//generateTest.show_1(person);
//使用这两个方法都可以成功
generateTest.show_2(apple);
generateTest.show_2(person);
//使用这两个方法也都可以成功
generateTest.show_3(apple);
generateTest.show_3(person);
}
}
public static <T> void print(T t){}
// 注意static位置在<T>前
Public static <T extends Generic> void print(T t){}
<E>
等同于<E extends Object>
Generic<E>
<E> void max(E o1,E o2)
<E extends Comparable<E>>
,这有两个含义,一是它指定E为Comparable的接口实例,其次,它指定Comparable进行比较的元素是E类型的。
package chapter19;
public class GenericSort {
public static void main(String[] args) {
// Create an Integer array
Integer[] intArray = { new Integer(2), new Integer(4), new Integer(3) };
// Create a Double array
Double[] doubleArray = { new Double(3.4), new Double(1.3), new Double(-22.1) };
// Create a Character array
Character[] charArray = { new Character('a'), new Character('J'), new Character('r') };
// Create a String array
String[] stringArray = { "Tom", "Susan", "Kim" };
// Sort the arrays
sort(intArray);
sort(doubleArray);
sort(charArray);
sort(stringArray);
// Display the sorted arrays
System.out.print("Sorted Integer objects: ");
printList(intArray);
System.out.print("Sorted Double objects: ");
printList(doubleArray);
System.out.print("Sorted Character objects: ");
printList(charArray);
System.out.print("Sorted String objects: ");
printList(stringArray);
}
/** Sort an array of comparable objects */
● public static <E extends Comparable<E>> void sort(E[] list) {
E currentMin;
int currentMinIndex;
for (int i = 0; i < list.length - 1; i++) {
// Find the minimum in the list[i..list.length-1]
currentMin = list[i];
currentMinIndex = i;
for (int j = i + 1; j < list.length; j++) {
if (currentMin.compareTo(list[j]) > 0) {
currentMin = list[j];
currentMinIndex = j;
}
}
// Swap list[i] with list[currentMinIndex] if necessary;
if (currentMinIndex != i) {
list[currentMinIndex] = list[i];
list[i] = currentMin;
}
}
}
/** Print an array of objects */
public static void printList(Object[] list) {
for (int i = 0; i < list.length; i++)
System.out.print(list[i] + " ");
System.out.println();
}
}
// 非受限通配
<?>
// 受限通配
<? extends SuperClass>
// 下限通配
<? super T>
创建一个数据结构就是创建这个类的一个实例。
Java中的两种容器
1.存储相同元素的合集collection
2.存储键值对的映射表map
add()
只能在子类里实现,因为它涉及到具体的类型。List
接口,AbstractList
抽象类,ArrayList
类。用户可以简单的定义一个具体类继承一个抽象类,这样就不用实现接口的所有方法。这些抽象类被称为便利抽象类。
所有的合集继承于Collection
接口,它提供了往合集中添加和删除元素的方法,如add,remove,retain
(并、差、交)和相应的addAll,removeAll,retainAll
,前者操纵单个对象,后者操纵整个合集。
AbstractCollection
实现了除add()
和iterator()
(迭代器)这种以外的所有方法。Iterator<E>
// 创建一个列表alist
ArrayList<String> alist = new ArrayList<String>();
alist.add("new york")
alist.add("new york2")
// 为alist生成一个迭代器
Iterator<String> alistRator = alist.iterator();
// 使用迭代器来访问列表
while(alistRator.hasNext()){
System.out.println("iterator.next().toUpperCase()" + " ");
}
//输出:new york new york2
ArrayList<String> alist = new ArrayList<String>();
alist.add("new york")
alist.add("new york2")
for(String a:alist){
System.out.println("a.toUpperCase()" + " ");
}
Collection
接口与Iterator
接口 ArrayList
和LinkedList
数组
是最优方案,如果需要改变长度,那么ArrayList
是最优方案,如果还想在表头修改数据,那么LinkedList
是最优方案。(注意:ArrayList也能在表头修改,但是效率会低一些,因为所有元素需要后移)listIterator()
方法和listIterator(startIndex)
方法,它们都继承了Iterator
接口并拓展它。它们会返回一个针对该列表元素的迭代器,并且支持双向遍历。 ArrayList
的动态创建: trimToSize()
方法将数组缩小到线性表的大小。ArrayList
不能在表头插入或删除元素。如果需要在表头插入元素,就需要用到LinkedList
类。
LinkedList
类用链表储存元素
// 非常低效
for (int i = 0; i < list.size(); i++){
a = get(i);
}
// 高效
for(int i : list){
a = i;
}
comparable
接口,可以通过里面的compareTo
方法来比较实现了comparable接口的两个元素。comparator
)来比较不同类的元素。
// 如果前面的小于后面的,返回一个负值,相等为0,大于为正值。
public int compare(T element1, T element2)
// 注意,该方法实现了Serializable接口来进行序列化,在实现comparator接口的地方最好都在后面跟上Serializable接口。
public class Geocom
● implements Comparator<GeometricObject>, java.io.Serializable{
public int compare(GeometricObject o1, GeometricObject o2){
double area1 = o1.getArea();
double area2 = o2.getArea();
if(area1 > area2)
return 1;
else if(area1 < area2)
return 0;
else
}
}
Collections.sort
// 使用Arrays的静态asList方法来方便的创建列表
List<String> list = Arrays.asList("red","green","blue");
// 使用sort静态方法来排序
Collections.sort(list);
// 使用重载的sort方法来调用reverseOrder比较器来倒序排序
Collections.sort(list, Collections.reverseOrder);
List<String> list1 = Arrays.asList("red","green","blue");
List<String> list2 = Arrays.asList("yellow");
Collections.copy(list1,list2);
// list1 : yellow green blue
栈是一种后进先出的结构。
Vector中的方法
import java.util.*;
public class PriorityQueueDemo {
public static void main(String[] args) {
● PriorityQueue<String> queue1 = new PriorityQueue<>();
queue1.offer("Oklahoma");
queue1.offer("Indiana");
queue1.offer("Georgia");
queue1.offer("Texas");
System.out.println("Priority queue using Comparable:");
while (queue1.size() > 0) {
System.out.print(queue1.remove() + " ");
}
● PriorityQueue<String> queue2 = new PriorityQueue<>(4, Collections.reverseOrder());
queue2.offer("Oklahoma");
queue2.offer("Indiana");
queue2.offer("Georgia");
queue2.offer("Texas");
System.out.println("\nPriority queue using Comparator:");
while (queue2.size() > 0) {
System.out.print(queue2.remove() + " ");
}
}
}
//输出结果:
> Priority queue using Comparable:
> Georgia Indiana Oklahoma Texas
> Priority queue using Comparator:
> Texas Oklahoma Indiana Georgia
Set<String> hashSet = new HashSet<>();
// 等价于
Set<String> hashSet = new HashSet<>(16, 0.75);
Ser<GeometricObject> set = new TreeSet<>(new GeoComparator);
// 默认按插入顺序排序
Map<String,Integer> linkmap = new LinkedHashMap<>();
// 按照访问顺序排序(最后访问的在最后面)
Map<String,Integer> linkmap = new LinkedHashMap<>(16, 0.75, true);
Map<String, Integer> tmap = new TreeMap<>();
Map<String, Integer> tmap = new TreeMap<>(new Comparator);
EMPTY_SET、EMPTY_LIST、EMPTY_MAP
O(logn)=O(log2 n)=O(loga n)
,所以常量的底可忽略。
public static int fib(int n){
if(n == 0)
return 0;
else if (n <= 2)
return 1;
else
return fib(n - 1) + fib(n - 2);
}
public static int fib(int n){
int f0 = 0;
int f1 = 1;
int f2 = 1;
if(n == 0)
return 0;
else if (n <= 2)
return 1;
for(int i = 3; i < n; i++){
f0 = f1;
f1 = f2;
f2 = f0 + f1;
}
}
public static int fib(int n, int ppre, int pre){
if(n <= 1)
return ppre;
else
return fib(int n - 1, int pre, int ppre + pre);
}
main{
fib(5, 1, 1);
}
用gcd(m, n)表示求m和n的最大公约数。
如果m % n=0,那么gcd(m, n)为n。
否则,gcd(m, n)就是gcd(n, m % n)。
// 时间复杂度为O(logn)
public static int gcd(int m, int n){
if(m % n == 0)
return n;
else
return gcd(n, m % n);
}
// 计算很多次
for(int i = 0; i < (int)math.sqrt(number); i++)
// 整个for循环只计算一次
int sqrtnum = (int)math.sqrt(number);
for(int i = 0; i < sqrtnum; i++)
O(n^2)
a[i]
为插入元素。将插入元素存储在临时变量cur
中。 (int j = i;;j--)
,这样在后移的时候不会覆盖后面的元素。 (a[j - 1])>cur
,则将该元素后移一位。 b[j - 1] = b[j]
,然后向前遍历j--
,直到前一位不比cur大为止b[j - 1] > cur
。 b[j]
的位置上空出来了,那么我们就插入这个空档b[j] = cur;
为什么是j?因为j-1小于cur
嘛,放到j-1肯定不对,放到j刚刚好。
public static void main(String[] args) {
int[] a = {1, 3, 5, 7, 24, 73, 72, 2};
int[] b = new int[8];// b数组的大小要和a一样。
int cur = 0;
for (int i = 0; i < a.length; i++) {
cur = a[i];
int j;
// important
for (j = i; j > 0 && b[j - 1] > cur; j--) { // 4
b[j] = b[j - 1];
}
b[j] = cur; // 5
}
for (int x : b) {
System.out.print(x + " ");
}
}
{1,3,5,2}
// 注意,这段代码的精妙之处就在于不需要创建子数组。
public static void insertionSort(int[] list){
for (int i = 0; i < list.length; i++) {
int cur = list[i];
int j;
// 注意,当i == j == 0时,for循环并不会执行,但是j = i仍然会发生,结果就是将第一个插入元素直接插入第一位。而这里并没有新数组,因此就是将list[0]的值赋给list[0]的无意义操作。
for (j = i; j > 0 && list[j - 1] > cur; j--) {
list[j] = list[j - 1];
}
list[j] = cur;
}
for (int x:list
) {
System.out.print(x + " ");
}
}
public static void main(String[] args) {
int[] a = {1, 3, 5, 7, 24, 73, 72, 2};
insertionSort(a);
}
public class BubbleSort {
public static void main(String[] args) {
int[] a = {6, 5, 7, 2, 9, 1, 0};
boolean b = true;
// 如果上次仍有排序,则继续循环,设置排序序号为false
for (int j = 0; j < a.length && b; j++) {
b = false;
//注意每次循环次数减一
for (int i = 1; i < a.length - j; i++) {
if (a[i - 1] > a[i]) {
// 交换元素
int temp = a[i];
a[i] = a[i - 1];
a[i - 1] = temp;
// 若本次仍存在冒泡,则继续大循环。
// 否则默认终止循环。
b = true;
}
}
}
for (int x : a) {
System.out.print(x + " ");
}
}
}
在最佳情况下,冒泡排序的时间为O(n),即一次全部冒泡成功。
最差情况为每次都要冒泡,为O(n^2)。
O(nlogn)
算法思路:
1.通过设计两个方法,一个是主排序方法,一个是单独的合并方法。在主方法中递归的调用分隔方法,直到分隔到底层,然后进行合并。
2.主方法中包含的方法:排序前半部分(递归1),排序后半部分(递归2),将两部分合并成一部分(合并方法)。
3.通过声明两个临时数组来存储数组的前半部分和后半部分。前半部分的数组长度直接通过list.length / 2
,但是后面的要用原长度减去前面的长度,而不能直接除以二,否则将会造成数据丢失。如原长度为9,简单的除以二的话是4,两个4的话,最后一个数据就丢失了。
4.在合并方法中,传入三个数组,前半部分,后半部分,总数组。并分别设定三个初始为0的int的下标指向他们。通过while循环重复比较两个部分的数组,并将其中较小的那个元素复制到总数组中,同时该数组和总数组的下标都自增1。
5.当其中一个数组的下标达到尽头时,跳出循环,通过后面的两个while循环将剩下那个数组的剩余元素复制进去就好,因为每次要合并的两个小数组都是排好序的,因此剩下的这些元素直接复制进去是没有问题的。
代码:
import java.util.Arrays;
public class MergeSort {
public static void main(String[] args) {
int[] a = {1, 3, 5, 2, 8};
mergeSort(a);
for (int x : a) {
System.out.print(x + " ");
}
}
public static void mergeSort(int[] list) {
if (list.length > 1) { // 递归结束条件
int[] firstHalf = new int[list.length / 2];
int[] secondHalf = new int[list.length - firstHalf.length];
// System里的数组复制方法,注意复制后半部分的时候,要从firstHalf.length开始复制(第二个参数),长度是secondHalf.length(最后一个参数)。
System.arraycopy(list, 0, firstHalf, 0, firstHalf.length);
System.arraycopy(list, firstHalf.length, secondHalf, 0, secondHalf.length);
mergeSort(firstHalf);
mergeSort(secondHalf);
merge(firstHalf, secondHalf, list);
}
}
public static void merge(int[] list1, int[] list2, int[] temp) {
int index1 = 0;
int index2 = 0;
int index3 = 0;
// 选取两个数组中较小的数放入最终数组,并在某个数组被读取完后停止。
while (index1 < list1.length && index2 < list2.length) {
if (list1[index1] < list2[index2])
temp[index3++] = list1[index1++];
else
temp[index3++] = list2[index2++];
}
// 将剩下的数直接复制进最终数组。
while (index1 < list1.length) {
temp[index3++] = list1[index1++];
}
while (index2 < list2.length) {
temp[index3++] = list2[index2++];
}
}
}
package demo2;
public class QuickSort {
public static void quickSort(int[] list) {
quickSort(list, 0, list.length - 1);
}
public static void quickSort(int[] list, int first, int last) {
// 对于每个快速排序,last必须大于first,如果last=-1,就结束递归。
if (last > first) {
// 返回主元的位置,然后通过主元分割前后两端,并依次进行快速排序。
int indexOfPivot = partition(list, first, last);
quickSort(list, first, indexOfPivot - 1);
quickSort(list, indexOfPivot + 1, last);
}
}
// 返回主元的正确位置,并交换处于不正确位置的元素,使得主元的前面都小于主元,后面都大于主元。
public static int partition(int[] list, int first, int last) {
int pivot = list[first];
int start = first++;
int temp = 0;
while (first < last) {
// 当发现前后两个特例之后,将他们交换。
// 如果只有小数列有特例,那么last会一直往前,越过大数列,停在小数列的最后一个位置。
// 这种情况大数列本身就是排好序的,因此将小数列中的特例与小数列的最后一位交换。
while (first < last && list[first] < pivot)
first++;
while (first < last && list[last] > pivot)
last--;
if (first < last) {
temp = list[last];
list[last] = list[first];
list[first] = temp;
}
}
// last的数小于等于pivot时停下。相当于将last放置在小数列的最后一个位置上,即大数列的第一个之前的那个位置。
while (list[last] > pivot) {
last--;
}
// 然后将last的数和第一个数(pivot)交换。
list[start] = list[last];
list[last] = pivot;
// 返回last现在所在的位置,也就是主元现在的位置。
return last;
}
public static void main(String[] args) {
int[] a = {5, 2, 9, 3, 8, 4, 0, 1, 6, 7};
quickSort(a);
for (int x : a) {
System.out.print(x + " ");
}
}
}
O(nlogn)
删除根元素
1.将最后一个元素复制到根元素位置,覆盖根元素。
2.将最后一个元素删除。
3.如果根元素小于两个子元素中较大的那个,将它们互换。
4.直到重新成为堆。
Heap类
package demo2;
public class Heap<E extends Comparable<E>> {
private java.util.ArrayList<E> list = new java.util.ArrayList<>();
public Heap() {
}
public Heap(E[] objects) {
for (int i = 0; i < objects.length; i++) {
add(objects[i]);
}
}
public void add(E newObject) {
list.add(newObject);
int cur = list.size() - 1;
while (cur > 0) {
int par = (cur - 1) / 2;
if (list.get(par).compareTo(list.get(cur)) < 0) {
E temp = list.get(cur);
list.set(cur, list.get(par));
list.set(par, temp);
} else
break;
// change par to cur.
cur = par;
}
}
public E remove() {
if (list.size() == 0) return null;
E removeObject = list.get(0);
list.set(0, list.get(list.size() - 1));
list.remove(list.size() - 1);
int cur = 0;
while (cur < list.size()) {
int son1 = cur * 2 + 1;
int son2 = cur * 2 + 2;
// 左子节点要严格小于list才不会越界
if (son1 >= list.size()) break;
// 找到两个子节点中比较大的
int max = son1;
if (son2 < list.size()) {
if (list.get(son1).compareTo(list.get(son2)) > 0)
max = son1;
else
max = son2;
}
if (list.get(cur).compareTo(list.get(max)) < 0) {
E temp = list.get(cur);
list.set(cur, list.get(max));
list.set(max, temp);
cur = max;
} else
break;
}
return removeObject;
}
public int getSize() {
return list.size();
}
}
package demo2;
public class HeapSort {
public static <E extends Comparable<E>> void heapSort(E[] list) {
// 通过list新建堆
Heap<E> a = new Heap<>(list);
// 注意,remove出来的是堆中最大的元素,因此要从后面开始遍历。
for (int i = list.length - 1; i >= 0; i--) {
list[i] = a.remove();
}
}
public static void main(String[] args) {
Integer[] a = {5, 2, 9, 3, 8, 4, 0, 1, 6, 7};
heapSort(a);
for (Integer x : a
) {
System.out.print(x + " ");
}
}
}
MyList
来声明相同的方法,然后让ArrayList和LinkedList接入接口后用不同的方式实现方法。MyAbstractList
来添加size
属性。MyAbstractList
中的size被声明为protected,protected一般很少使用,但在这里很合适,因为它的子类可以访问大小,而其他包中的非子类并不能访问它。作为一个常用规则,可以将抽象类中的数据域声明为被保护的。
E[]
数组来存储,存储的是引用。廖雪峰的官方网站:https://www.liaoxuefeng.com/wiki/1252599548343744/1264799402020448
class
创建了对应的Class
实例,并在实例中保存了该class
的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等,因此,如果获取了某个Class
实例,我们就可以通过这个Class
实例获取到该实例对应的class
的所有信息。
TaskClass task = new TaskClass();// TaskClass类实现了Runnable接口
Thread thread = new Thread(task);// 通过TaskClass创建一个线程
thread.start();// 启动它
MyThread mythread = new MyThread();// Myshread类继承了Thread类
mythread.start();// 直接启动子类就好
Thread.yield()
方法用来暂时将时间让给其他线程。Thread.sleep()
方法可以让线程暂时休眠几毫秒。注意:使用Thread.sleep()方法时,必须捕获InteruptedException。所以要将代码放到tryCatch块中。如果一个循环中调用了sleep方法,那就要将整个循环放入catch块中。Thread.join()
方法可以让这个线程插队,其他线程会等待调用join的线程跑完再开始工作。setPriority()和getPriority()
可以查看与设置优先级(0-10),优先级较高的线程会优先运行,如果一个高优先级的线程一直运行,会发生资源匮乏,因此,高优先级的线程必须定时的调用sleep()或yield()
方法来让出资源。
new Thread(new Runnable(){}).start();
// 填充run()方法
new Thread(new Runnable(){
@Override
public void run(){
System.out.println("nihao");
}
}).start();
// 可以使用Lambda表达式进一步简化,将new Runnable(){} 变成()-> {}
// 并且不用写run()的方法头!
new Thread(()->{
System.out.println("nihao");
}).start();
newFixedThreadPool(int n)
用来创建一个最多运行n线程的线程池。如果一个线程60s没有被调用,则杀死他。newCachedThreadPool()
创建一个灵活的线程池。
ExecutorService executor = Executors.newFixedThreadPool(3);
// 如果设置为1,那么三个线程将串行执行。
// 如果是newCachedThreadPool(),所有任务都并发执行。
executor.execute(new PrintChar('a', 100));// +execute(Runnable object):void
executor.execute(new PrintChar('b', 100));
executor.execute(new PrintNum(100));
executor.shutdown();
synchronized(expr){ // expr必须是一个对象的引用
//...
}
synchronized(account){
account.toString();
}
将synchronized后面的括号中填上this,就可以锁定当前对象或者当前类。
private static Lock lock = new ReentrantLock();
lock.lock();
try{
}catch{
}finally{
lock.unlock();
}
尽量把锁的释放放在finally里,这样可以确保锁被释放。
例如,我们想通过两个线程,一个提款,另一个存款,在单独行动的时候另一个都不能打扰,而在某些情况下又想把锁移交过去,比如提款余额不足的时候就调用存款线程,先存进去,然后再调用取款线程,尝试取款。
代码实现
private static Lock lock = new ReentrantLock();
private static Condition newDeposit = lock.newCondition();
https://www.cnblogs.com/cjsblog/p/9078341.html