Java语法 1小时入门

2019-10-22  本文已影响0人  彡廿

1、基本数据类型

1.1 Java是强类型语言

1.2 Java的类型分类

public class Main {

    public static void main(String[] args) {
        //  基本数据类型
        byte a_byte = 1;
        short a_short = 1;
        int a_int = 1;
        long a_long = 1;
        float a_float = 1.0f;
        double a_double = 1.0;
        boolean a_boolean = true;
        char a_char = 'c';
    }
}

1.3 类型转换

1.3.1 自动类型转换

范围小可以自动转化为范围大的数据类型。

1.3.2 强制类型转换

范围大的向范围小转换。

语法:

(targetType)value

1.3.3 表达式类型的自动提升

1.4 直接量

能指定直接量的三种类型: 基本类型,字符串类型,null类型。

tips: 如果程序第一次使用字符串直接量,java会使用常量池(constant pool)来缓存改字符串直接量。后面在使用该字符串直接量,会直接使用常量池中的字符串直接量。

1.5 运算符

1.5.1 算术运算符

+ - * / += -= *= /=

2、流程控制

public static void main(String[] args) {
    // if else
    int score = 50;
    if (score > 90) {
        System.out.println("优秀");
    } else if (score > 70) {
        System.out.println("中等");
    } else if (score > 60) {
        System.out.println("及格");
    } else {
        System.out.println("差的");
    }

    //  只能是 byte short char int enum String 不能是boolean
    //  可以省略 case 后面的花括号
    switch (score) {
        case 50:
            System.out.println("50分");
            System.out.println("不够优秀");
            break;
        default:
            System.out.println("考了 = " + score);
            System.out.println("优秀吗");
            break;
    }

    // while
    int count = 10;
    while (count < 20) {
        System.out.println("---------" + count);
        count++;
    }

    // do while
    do {
        System.out.println("----------" + count);
        count--;
    } while (count < 10);

    // for
    for (int i = 0; i < 10; i++) {
        System.out.println("i = " + i);
    }
}

3、 面向对象

3.1) 基本语法


public class Person {
    // filed
    private static String country;
    private int age = 0;
    private float height;
    private boolean sex;

    // 非静态变量的初始化代码块
    {
        System.out.println("非静态初始化代码块");
        age = 10;
        height = 12.9f;
        sex = true;
    }

    // 静态变量的初始化块
    static {
        System.out.println("静态初始化代码块");
        country = "china";
    }

    // 构造方法,如果不定义构造方法,系统自动会生成一个默认的没有参数的构造方法
    public Person(int age, float height, boolean sex) {
        this.age = age;
        this.height = height;
        this.sex = sex;
    }

    // 静态方法
    public static void say() {
        System.out.println("I'm " + country);
    }

    // 可变参数,values相当于一个数组
    public void eat(Object... values) {
        for (Object value : values) {
            System.out.println(value);
        }
    }

    // 方法的重装,方法名相同,参数列表不同,返回值类型,和修饰符和方法重载没有关系
    public void work(String job) {
        System.out.println("job is" + job);
    }

    public void work(String job, String where) {
        System.out.println("at " + where + "job is" + job);
    }

    @Override
    public String toString() {
        return ("age =" + age + "height =" + height + "sex =" + sex);
    }

    public static void main(String[] args) {
        Person person = new Person(10, 121.0f, false);
        System.out.println(person);
    }
}

局部变量定以后,必须显示初始化才能使用。

成员变量定义后,不必初始化,系统会给一个默认值。

3.1.1)访问权限修饰符:

public > protected > default > private

一个类就是一个小模块,在程序设计时,应尽量避免一个模块直接操作和访问另一个模块的数据,模块设计要求高内聚,低耦合。

// 声明包
package 包名

// 导包
import 包
// 静态导入,导入静态变量,静态方法
import static 包

java中常用的包

java.lang
java.util
java.net
java.io
java.text
java.sql

3.1.2)继承

java的类只能有一个直接父类。

子类可以复写父类的方法,遵循一个原则:两同两小一大。

两同:方法名相同,形参列表相同。
两小:子类方法返回值类型应比父类方法返回值类型更小或者相等,子类方法声明抛出的异常比父类方法声明抛出的异常更小或者相等。
一大:子类方法的访问权限应比父类方法的访问权限更大或者相等。

3.1.3 ) super


public class SubClass extends SuperClass {
    private int a;

    public SubClass(int a) {
        // 调用父类被复写的方法
        super(a);
    }

    @Override
    public void test() {
        // 调用父类被隐藏的变量
        System.out.println(super.a);
    }
}

class SuperClass {
    public int a;

    public SuperClass(int a) {
        this.a = a;
    }

    public void test() {
        System.out.println(a);
    }
}

3.2) 多态

引用变量有两种类型:一个编译时类型,一个是运行时类型。


class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public void say() {
        System.out.println("I'm " + name);
    }
}

public class Man extends Person {
    private int age;

    public Man(String name, int age) {
        super(name);
        this.age = age;
    }

    @Override
    public void say() {
        super.say();
        System.out.println("age is " + age);
    }

    public static void main(String[] args) {
        // 编译时 Person 运行时 Man
        Person person = new Man("wangbo", 20);
        person.say();
    }
}

3.2.1)引用变量的强制类型转换

3.2.2)instanceof 运算符

注意:

null instancof Person //返回永远为false,null不是
if (null instanceof Person){
    System.out.println(true);
} else {
    System.out.println(false);
}

继承破坏封装,建议采用组合实现复用

3.3 初始化块

它是类的第四个成员(成员变量,方法,构造器)。初始化块和构造器的作用类似,但比构造器率先执行。

static {
  // 静态初始化块,对类进行操作,同样不能访问非静态成员
}

{
  // 普通初始化块,对对象进行操作
}

执行顺序:

静态初始化块 --> 前面执行(普通初始化块,声明实例变量指定的默认值) --> 构造器

普通初始化块和声明实例变量指定的默认值,谁在前面谁先执行。

3.3.1) 初始化块与构造器

java文件

public class InstanceInit {
  // 初始化块和声明默认初始化,谁在后面谁的值放到构造器中
    {
        a = 6;
    }
    private int a = 9;

    public static void main(String[] args) {
        System.out.println(new InstanceInit().a);
    }
}

class文件

public class InstanceInit {
    private int a = 6;

    public InstanceInit() {
        this.a = 9;
    }

    public static void main(String[] args) {
        System.out.println((new InstanceInit()).a);
    }
}

3.4 包装类

基本数据类型对应的类。

byte -> Byte
short -> Short
int -> Integer
boolean -> Boolean
long -> Long
char -> Character
float -> Float
double -> Double

JDK1.5之前,装箱和拆箱比较麻烦,JDK1.5之后能够自动装箱和拆箱

3.4.1) 自动装箱和拆箱

// 自动装箱
Integer inObj = 8;
Object boolObj = true;

// 自动拆箱
int it = inObj;

3.4.2)String与基本类型之间的转换

// 字符串转基本类型
String intStr = "123";
int it1 = Integer.parseInt(intStr);
int it2 = new Integer(intStr);

// 基本类型转换字符串
String ftStr = String.valueOf(2.35f);

3.5 ) toString、== 、equals方法

toString方法在直接打印对象时,自动调用的方法。一般会自定义,来输出想输出的属性

==

equals()

public class Person {
    private String name;
    private String idStr;

    public Person(String name, String idStr) {
        this.name = name;
        this.idStr = idStr;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        // instanceof 运算符前面对象是后面类的实例活其子类的实例都返回true,所有用instanceof判断对象是否为同一个可能有问题
        if (obj != null && obj.getClass() == Person.class) {
            Person person = (Person) obj;
            if (this.idStr.equals(person.idStr)) {
                return true;
            }
            return false;
        }
        return false;
    }
}

3.6)类成员

java类里只能含有成员变量,方法,构造器,初始化块,内部类(接口,枚举)五种。

3.7)final修饰符

final关键字可用于修饰类、变量和方法。

3.7.1)final成员变量

final变量获得初始值之后不能被重新赋值。

final修饰的成员变量必须由程序员指定初始值,系统不会指定默认值。

分析原因:

final修饰的成员变量,如果不指定初始值,系统分配默认值,之后不能修改。这些成员变量也就失去了意义,所以系统要求final成员变量必须主动初始化。

3.7.2)final修饰局部变量

局部变量系统不会提供默认值,final修饰的局部变量也可以不指定默认值,后面再指定。

public class Person {
    
    public void test(final int a){
        // 不能对final修饰的形参赋值
        // a = 5;
    }
    
    public static void main(String[] args) {
        final String name;
        name = "hello";
        System.out.println(name);
    }
}

3.7.3)final修饰基本类型变量和引用类型变量的区别

public class Person {
    public static void main(String[] args) {
        // 修饰基本类型,不能修改值
        final int age = 10;
        // age = 11;
        
        // 修饰引用类型
        final List<String> stringList = new ArrayList<>();
        
        // 引用类型的指针地址不变,内容改变
        for (int i = 0; i < 10; i++) {
            stringList.add("--" + i);
        }
    }
}

3.7.4) final 方法

final修饰的方法不能被复写,但是可以重载。

public class FinalMethodTest {
    public final void test(){}
}

class Sub extends FinalMethodTest{
    // 下面方法定义将编译出错,不能复写final方法
    public void test(){}
}
public class FinalMethodTest {
    private final void test(){}
}

class Sub extends FinalMethodTest{
    // 父类private的方法,子类不可见,子类中的test是重定义的
    private void test(){}
}

3.7.5)final类

final类不能被继承。

3.7.6)不可变类

一般不可变量用于数据层的itemInfo。不可变类的引用类型的成员变量,在赋值的时候最好复制一份,避免直接赋值,因为外部可能修改这个引用类型的变量。

public class ImmutableClass {
    private final int id;
    private final String name;

    public ImmutableClass(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        ImmutableClass that = (ImmutableClass) o;
        return id == that.id &&
                Objects.equals(name, that.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}

3.8)抽象类

抽象类是从多个类中抽象出来的模板。

抽象类的作用: 提供模板,避免子类设计的随意性。

3.8.1)定义

public class Man extends Person {
    @Override
    void eat(String food) {
        System.out.println("eat");
    }

    @Override
    void work() {
        System.out.println("work");
    }
}

abstract class Person {
    abstract void eat(String food);

    abstract void work();

    void sleep() {
        System.out.println("睡觉");
    }
}

3.9)接口

类是具体的实现,接口定义时多个类共同的公共行为规范,是与外部交流的通道。

软件设计:通常会采用面向结构的设计,来实现模块之间的低耦合,同时可扩展性和可维护性更好。

接口的特性:

3.9.1)定义

public interface Output {
    // int MAX_CACHE_LINE = 50; 系统会自动为接口定义的成员变量增加public static修饰符
    public static final int MAX_CACHE_LINE = 50;

    // 接口定义的普通方法默认是public抽象方法
    void out();

    void getData(String msg);

    // 接口里的默认方法,需要使用default修饰
    default void print(String... msgs) {
        for (String msg : msgs) {
            System.out.println(msg);
        }
    }

    // 接口里的类方法,需要static修饰
    static String staticTest() {
        return "接口里的方法";
    }
}

3.9.2)接口和抽象类的区别

3.10)内部类

成员内部类,局部内部类,匿名内部类,静态内部类,非静态内部类。

静态内部类

匿名内部类:

3.11)Lambda表达式

3.12)引用方法

className::类方法
object::实例方法
className::new

3.13)枚举类

public enum SearchType {
  // 此处的枚举值必须调用对应的构造函数来创造
    All("all"),
    Product("product"),
    Article("review");

  // 枚举成员一般不可变,这样更安全
    private final String mId;

    SearchType(String id) {
        mId = (id == null ? "" : id.trim().toLowerCase(Locale.US));
    }

    public String getId() {
        return mId;
    }

    public static SearchType getById(String id) {
        id = (id == null ? "" : id.trim().toLowerCase(Locale.US));
        for (SearchType type : SearchType.values()) {
            if (id.equals(type.mId)) {
                return type;
            }
        }

        return null;
    }
}

3.14)对象与垃圾回收

3.14.1)对象在内存中状态

[图片上传失败...(image-25ef57-1571732324683)]

3.14.2)强制垃圾回收

程序无法精确控制java垃圾回收机制,但是可以强制系统进行垃圾回收 ——这种强制只是通知系统进行垃圾回收,但系统是否进行垃圾回收依然不确定。

finalize()方法


public class FinalizeClass {
    private static FinalizeClass finalizeClass = null;

    public void info() {
        System.out.println("测试资源清理的finalize方法");
    }

    public static void main(String[] args) {
        new FinalizeClass();
//        通知系统进行资源回收
        System.gc();
//        强制垃圾回收机制调用可恢复对象的finalize()方法
//        Runtime.getRuntime().runFinalization();
        System.runFinalization();

        finalizeClass.info();
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        // 让对象从可恢复状态变成了可达状态,内存泄漏
        finalizeClass = this;
    }
}

3.15)对象的强、软、弱、虚引用

软、弱、虚都有一个get()方法,用于获取他们所引用的对象。

4、泛型

4.1) 定义泛型类和接口

public interface Map<K, V> {
    Set<K> keySet();

    V put(K key, V value);
}
// 设定泛型上限
public class Apple<T extends Number> {
    private T info;

    public Apple(T info) {
        this.info = info;
    }

    public Apple() {
    }

    public T getInfo() {
        return info;
    }

    public void setInfo(T info) {
        this.info = info;
    }

    public static void main(String[] args) {
        // 菱形语法
        Apple<String> apple = new Apple<>("苹果");
        Apple<Double> doubleApple = new Apple<>(13.0);
        
    }
}

4.2)泛型类派生子类

// public class A extends Apple<T> 错误的做法
// 派生类需要指定具体类型
public class A extends Apple<String>

public class A extends Apple

注意

List<A> 并不是 List<Apple>的子类
List<Apple> list1 = new List<>();
List<A> list2 = new List<>();
List<String> list3 = new List<>();

void test(List<Apple> list){
  // do something
}

test(list1); // 正确
test(list2); // 错误
test(list3); // 错误

// ? 泛型通配符
void print(List<?> list){
   // do something
}

print(list1); // 通过
print(list2);  // 通过
print(list3); // 通过

// 限定 ? 泛型通配符
void say(List<? extend Apple> list){
  // do something
}

say(list1); // 通过
say(list2); // 通过
say(list3); // 错误

4.3)泛型方法

修饰符 <T,S> 返回值类型<N,M> 方法名(形参列表){

}
public class TFuncation {
    static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
        for (T o : a) {
            c.add(o);
        }
    }

    public static void main(String[] args) {
        Object[] oa = new Object[100];
        Collection<Object> co = new ArrayList<>();
        fromArrayToCollection(oa, co);
    }
}

5、基础类库

5.1)系统相关

System

// 获取所有系统变量
System.getenv();
// 获取指定系统变量
System.getenv("JAVA_HOME");
// 获取所有系统属性
System.getProperties();
// 获取指定系统属性
System.getProperty();
// 通知系统进行垃圾回收的
System.gc();
// 通知系统进行资源清理
System.runFinalization();
// 获取系统当前的时间,时间粒度取决于操作系统,
System.currentTimeMillis();  // 毫秒为单位,不准确,操作系统以几十毫秒为单位
System.nanoTime();  // 纳秒为单位,很少用,操作系统不支持

// 根据对象地址计算得到的hashCode值
System.identityHashCode(Object x);

Runtime

// 通知系统进行垃圾回收
Runtime.gc();
// 清理系统资源
Runtime.runFinalization();
// 加载文件
Runtime.load(String fileName);
// 加载动态链接库
Runtime.loadLibrary(String libname);

public class RuntimeTest {
    public static void main(String[] args) {
        // 获得运行时队形
        Runtime runtime = Runtime.getRuntime();
        //  处理器数量
        System.out.println(runtime.availableProcessors());
        // 空闲内存数
        System.out.println(runtime.freeMemory());
        // 总内存数
        System.out.println(runtime.totalMemory());
        // 最大内存数
        System.out.println(runtime.maxMemory());
    }
}

5.2)Clone


public class UserTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        User user = new User(2);
        User user1 = user.clone();
        System.out.println(user == user1);  // false
        System.out.println(user.address == user1.address);  // true
    }
}

class Address {
    String detail;

    public Address(String detail) {
        this.detail = detail;
    }
}

class User implements Cloneable {
    int age;
    Address address;

    public User(int age) {
        this.age = age;
        address = new Address("北京海淀");
    }

    public User clone() throws CloneNotSupportedException {
        return (User) super.clone();
    }
}

clone只是一种浅克隆,只是对引用变量进行复制,对引用变量所引用的对象没有复制。引用变量所指向的内存还是同一个实例。

5.3)新增Objects工具类

Java为工具类的命名习惯是添加一个字母s。Arrays,Collections。

public class ObjectsTest {
    static ObjectsTest objectsTest;

    private String name;

    public ObjectsTest(String name) {
        // 用来对方法形参进行输入校验,并自定义空指针异常
        this.name = Objects.requireNonNull(name, "name 不能为null");
    }

    public static void main(String[] args) {
        System.out.println(Objects.hashCode(objectsTest));
        System.out.println(Objects.toString(objectsTest));
        System.out.println(Objects.requireNonNull(objectsTest, "参数不能为null"));
    }
}
public static <T> T requireNonNull(T obj, String message) {
        if (obj == null)
            throw new NullPointerException(message);
        return obj;
    }

5.4)StringBuffer类

5.5)随机数

// 当前时间为随机数种子
Random random = new Random(System.currentTimeMillis());
int val = random.nextInt(10);
// 线程安全的随机数
ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
int val2 = threadLocalRandom.nextInt(10, 100);

5.6)BigDecimal类

BigDecimal f1 = new BigDecimal("0.05");
BigDecimal f2 = BigDecimal.valueOf(0.01);
// 不推荐使用
BigDecimal f3 = new BigDecimal(0.05);
System.out.println("0.05 + 0.01 = " + f1.add(f2));  // 0.06
System.out.println("0.05 - 0.01 = " + f1.subtract(f2)); // 0.04
System.out.println("0.05 * 0.01 = " + f1.multiply(f2)); // 0.0005
System.out.println("0.05 / 0.01 = " + f1.divide(f2)); // 5

5.7)Calendar

5.8)正则表达式

 // 将字符串编译成pattern对象
Pattern p = Pattern.compile("a*b");
Matcher m = p.matcher("aaaabbbb");
boolean b = m.matches();

6、集合

数组是不可变的,集合是可变的。

6.1)集合的常见操作

集合是实现了Collection的接口的,Collection是继承了interator接口的。

Collection collection = new ArrayList();
collection.add("hello");
// 自动装箱
collection.add(6);
//删除指定元素
collection.remove(6);

Collection books = new HashSet();
books.add("轻量级java EE 企业应用之战");
books.add("疯狂Java讲义");

// 删除集合
collection.removeAll(books);

// 清空
collection.clear();
// books集合里剩下Collection也包含的集合
books.retainAll(collection);

// lambda表达式遍历集合
books.forEach(obj -> System.out.println(obj));

6.2)Iterator遍历集合元素

 // 迭代器遍历集合,并删除,迭代只是把集合元素的值传递给了迭代变量,修改迭代变量的值对集合元素本身没任何影响
Iterator iterator = books.iterator();
while (iterator.hasNext()) {
    String book = (String) iterator.next();
    if (book.equals("疯狂Java讲义")) {
        iterator.remove();
        // books.remove(book)将引发ConcurrentModificationException异常
    }
    book = "测试字符串";
}

6.3)HashSet类

HashSet集合判断两个元素相等的标准是两个对象通过equals()并且两个对象的hashCode()方法返回值也相等。

6.3.1) LinkedHashSet类

6.3.2)TreeSet类

6.3.4)HashSet和TreeSet性能对比

6.4)List集合

7、异常处理

7.1)异常概念

异常处理让程序具有极好的容错性和健壮性。发生异常系统自动生成一个Exception对象来通知程序。

java的异常分为Checked异常和Runtime异常。Checked异常可以在编译阶段被发现。Runtime异常只能在运行期间得到处理。

try {
    // 异常由运行时环境抛出
    int a = 0 / 2;
} catch (Exception e) {
    // 处理异常
} finally {
    // 无论如何都会执行
}

try业务逻辑出现异常,系统自动生成异常对象,该异常对象被提交给Java运行时环境,这个过程为抛出(throw)异常。Java运行时环境受到异常,寻找能处理该异常的catch块,找到就交给catch处理,找不到运行时环境终止,Java程序退出。

7.2)异常类

都继承Throwable。

先捕获范围小的异常,再捕获异常范围大的。

getMessage():异常详细描述
printStackTrace(): 输出跟踪信息栈
printStackTrace(PrintStream s): 输出跟踪信息栈到s流
getStackTrace(): 获得跟踪栈信息

8、注解

注解是一种接口。都有一个 value属性。

JDK5增加了元数据(MetaData)支持,也就是Annotation注解。

注解和Python的装饰器一样的功能,在代码里添加标记,在编译、类加载、运行时被读取,并执行相应的处理。通过注解可以在不改变原有逻辑的情况下,在源文件里添加一些信息。

8.1)基本Annotation

9、IO

输入流和输出流

10、多线程

并行和并发

10.1)线程的创建和启动

10.1.1)继承Thread类创建线程类

所有的线程都必须是Thread类或其子类的实例。

  1. 定义Thread类,并重写run()方法,run()方法体代表了线程需要完成的任务。
  2. 创建Thread子类实例
  3. 调用实例的start()方法启动该线程
new Thread() {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("thread name " + this.getName() + i);
        }
    }
}.start();
// 当前运行的线程
Thread.currentThread()
// 获取当前线程的名字
.getName() 
// 设置当前线程的名字
setName(name)

10.1.2)实现Runnable接口创建线程类

new Thread(new Runnable() {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("thread Name " + Thread.currentThread().getName() + i);
        }
    }
}).start();

Runnable接口创建的多线程,可以共享线程的实例变量。

10.1.3)使用Callable和Future创建线程

Callable相当于Runnable的增强版。里面的call()方法可以作为线程的执行体。call()更强大。

Callable对象不能直接作为Thread的target。

// FutureTask 包装 Callable,FutureTask继承Runnable
FutureTask<Integer> futureTask = new FutureTask<>(new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println("current thread name " + Thread.currentThread().getName() + " " + i);
        }
        return 1000;
    }
});

new Thread(futureTask).start();
try {
    // 获取返回值
    System.out.println(futureTask.get());
} catch (InterruptedException e) {
    e.printStackTrace();
} catch (ExecutionException e) {
    e.printStackTrace();
}

10.2)线程的生命周期

启动线程用start()方法,而不是run()方法。直接调用run()方法是不是启动线程的。也不要连续调用两次start()方法。

[图片上传失败...(image-51f2bf-1571732324683)]

10.3)控制线程

10.3.1) join

public class JoinThread extends Thread {
    @Override
    public void run() {
        super.run();
        for (int i = 0; i < 100; i++) {
            System.out.println("JoinThread " + i);
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            if (i == 20) {
                JoinThread joinThread = new JoinThread();
                joinThread.start();
                try {
                  // 在main线程中调用了joinThread的join()方法,main线程必须等待joinThread执行结束才会向下执行
                    joinThread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("Main Thread " + i);
        }
    }
}

10.3.2)后台线程(Daemon Thread)

在后台运行,为其他线程提供服务。

特征:所有的前台线程都死亡了,后台线程会自动死亡。

调用Thread对象setDaemon(true) 方法可以将指定的线程设置成后台线程。

public class DaemonThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println(getName() + i);
        }
    }

    public static void main(String[] args) {
        DaemonThread daemonThread = new DaemonThread();
        // 在start之前指定线程设置成后台线程
        daemonThread.setDaemon(true);
        daemonThread.start();
        for (int i = 0; i < 4; i++) {
            System.out.println(Thread.currentThread().getName() + i);
        }
    }
}

isDaemon() 判断线程是否为后台线程。

前台线程死亡后,JVM会通知后台线程死亡,但它收到指令做出响应,需要一定时间。

10.3.3)线程睡眠:sleep

Thread.sleep(1000) 静态方法让当前执行的线程休眠。

public class SleepThread {
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            System.out.println("当前时间:" + new Date());
            Thread.sleep(1000);
        }
    }
}

10.3.4)线程让步:yield

yield()方法和sleep()方法相似。也是Thread的静态方法,他可以让当前线程停止,但不会阻塞当前线程,他是将线程转入就绪状态。

public class YieldThread extends Thread {
    private YieldThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println(getName() + i);
            if (i == 20) {
                Thread.yield();
            }
        }
    }

    public static void main(String[] args) {
        YieldThread yieldThread = new YieldThread("高级");
        yieldThread.setPriority(Thread.MAX_PRIORITY);
        yieldThread.start();

        YieldThread yieldThread1 = new YieldThread("低级");
        yieldThread1.setPriority(Thread.MIN_PRIORITY);
        yieldThread1.start();
    }
}

sleep比yield有更好的可移植性。

10.3.5)改变线程优先级

每个线程都有优先级,默认和创建它的父线程的优先级相同。优先级高获得更多的执行机会。main线程具有普通优先级。

thread.setPriority()
int priotity = yieldThread.getPriority();
public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;

10.4)线程同步

两个线程并发访问修改同一个文件,就可能造成异常。这就是线程安全问题。

10.4.1)同步代码块

synchronized(obj){
  // 同步代码块
}

obj是同步监视器,线程开始执行同步代码块之前,必须先获得同步监视器的锁定。任何时刻只能有一个线程可以获得对同步监视器的锁定,当同步代码执行完成之后,该线程会释放对该同步监视器的锁定。

Java允许任何对象作为同步监视器的锁定,但建议将并发访问的共享资源作为同步监视器。

public class SysncThread extends Thread {
    private Account account;
    private int money;

    public SysncThread(String name, Account account, int money) {
        super(name);
        this.account = account;
        this.money = money;
    }

    @Override
    public void run() {
      // 符合加锁 -- 修改 -- 释放锁的逻辑,一般是要操作的对象作为同步监视器。
        synchronized (account) {
        for (int i = 0; i < 10; i++) {
            if (account.getMoney() > money) {
                account.removeMoney(money);
                System.out.println("当前余额:" + account.getMoney());
            } else {
                System.out.println("余额不足,取钱失败");
            }
        }
        }
    }

    public static void main(String[] args) {
        Account account = new Account(1000);
        new SysncThread("A", account, 100).start();
        new SysncThread("B", account, 100).start();
    }
}

class Account {
    private int money;

    public int getMoney() {
        return money;
    }

    public void removeMoney(int money) {
        this.money -= money;
    }

    public Account(int money) {
        this.money = money;
    }
}

10.4.2)同步方法

同步方法就是使用synchronized关键字来修饰某个方法,则该方法成为同步方法。被synchronized修饰的方法,无须指定同步监视器,同步方法的同步监视器就是this。

public class SysncThread extends Thread {
    private Account account;
    private int money;
    private Object object = new Object();

    public SysncThread(String name, Account account, int money) {
        super(name);
        this.account = account;
        this.money = money;
    }

    // 可以去掉同步代码块了
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            account.removeMoney(money);
        }
    }

    public static void main(String[] args) {
        Account account = new Account(1000);
        new SysncThread("A", account, 100).start();
        new SysncThread("B", account, 100).start();
    }
}

class Account {
    private int money;

    public int getMoney() {
        return money;
    }

    // 同步方法:多线程操作访问的方法
    public synchronized void removeMoney(int money) {
        if (this.money >= money) {
            this.money -= money;
            System.out.println("当前余额:" + this.money);
        } else {
            System.out.println("当前余额不足");
        }
    }

    public Account(int money) {
        this.money = money;
    }
}

synchronized尽量减少作用域。

可变类的线程安全是降低程序运行效率为代价的。为了减少线程安全带来的负面影响,程序可以采用的策略。

10.4.3)释放同步监视器的锁定

同步代码块同步方法在执行之前必须先获得同步监视器的锁定。如何释放同步监视的锁定呢。

如何几种情况线程不会释放同步监视器

10.4.4)同步锁(Lock)

通过显示定义同步锁对象来实现同步。
Lock提供了比同步方法,同步代码块更广泛的操作,更灵活的结果。

ReadWriteLock(读写锁),ReentrantLock(可重入锁),StampedLock类。

class Account {
        //定义锁
    private final ReentrantLock lock = new ReentrantLock();
    private int money;

    public int getMoney() {
        return money;
    }

    // 同步方法:多线程操作访问的方法
    public void removeMoney(int money) {
            // 加锁
        lock.lock();
        try {
            if (this.money >= money) {
                this.money -= money;
                System.out.println("当前余额:" + this.money);
            } else {
                System.out.println("当前余额不足");
            }
        } finally {
                // 释放锁
            lock.unlock();
        }
    }

    public Account(int money) {
        this.money = money;
    }
}

使用ReentrantLock对象来进行同步,加锁和释放所出现在不同的作用范围内时,建议用finally块来确保必要时释放锁。

10.5)volatile关键字

volatile关键字为实例的同步访问提供了免锁机制。如果一个filed声明为volatile,那么编译器和虚拟机就知道改filed可能是被另一个线程并发更新。

10.5.1)Java内存模型

10.5.2)原子性,可见性,有序性

volatile,不保证原子性,保证有序性和可见性。

volatile 在保证有序性方面的性能要高于synchronized,volatile是修饰一个filed的,s'ynchronized修饰一段代码或函数。

10.5)线程通信

10.5.1)传统线程通信

借助Object类提供的 wait() notify() notifyAll() 三个方法。这三个方法必须由同步监视器对象来调用。

wait(): 导致当前线程等待,直到调用了notify() notifyAll() 方法。wait()可以带时间参数,表示经过这么长时间会自动唤醒。
notify(): 唤醒此同步监视器上等待的线程,唤醒那个线程是任意的。
notifyAll(): 唤醒此同步监视器上等待的所有线程。
class Account {
    private int money;
    private boolean flag;

    public Account(int money) {
        this.money = money;
    }

    public int getMoney() {
        return money;
    }

    public synchronized void draw(int drawAmount) {
        try {
            if (!flag) {
                wait();
            } else {
                System.out.println(Thread.currentThread().getName() + "取钱:" + drawAmount);
                money -= drawAmount;
                System.out.println("余额:" + money);
                flag = false;
                notifyAll();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void deposit(int depositAmount) {
        try {
            if (flag) {
                wait();
            } else {
                System.out.println(Thread.currentThread().getName() + "存钱操作" + depositAmount);
                money += depositAmount;
                System.out.println("余额:" + money);
                flag = false;
                notifyAll();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

10.5.2) 使用Condition控制线程通信

如果使用synchronized关键字保证同步,而是用Lock保证同步,则系统中不存在同步监视器,也就不能用wait() notify() notifyAll() 方法进行线程通信了。

使用Lock对象保证线程同步,可以使用Condition对象来保持协调。
Condition将同步监视器方法 wait() notify() notifyAll() 分解成截然不同的对象。

Condition对象绑定在Lock对象上,可以 lock.newCondition()来获得Conditaion对象。

Condition对象的方法

await(): 类似同步监视器的 wait() 方法,也有类似的等待多久的await()方法。
sigal(): 唤醒在此Lock对象上等待的单个线程。选择哪个一线程是任意的。
sigalAll(): 唤醒在此Lock对象上等待的所有线程。

​```java
class Account {
        private Lock lock = new ReentrantLock();
        private Condition condition = lock.newCondition();
        private int money;
        private boolean flag;

        public Account(int money) {
            this.money = money;
        }

        public int getMoney() {
            return money;
        }

        public void draw(int drawAmount) {
            lock.lock();
            try {
                if (!flag) {
                    condition.wait();
                } else {
                    System.out.println(Thread.currentThread().getName() + "取钱:" + drawAmount);
                    money -= drawAmount;
                    System.out.println("余额:" + money);
                    flag = false;
                    condition.signalAll();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

        public void deposit(int depositAmount) {
            lock.lock();
            try {
                if (flag) {
                    condition.await();
                } else {
                    System.out.println(Thread.currentThread().getName() + "存钱" + depositAmount);
                    money += depositAmount;
                    System.out.println("余额:" + money);
                    flag = true;
                    condition.notifyAll();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

10.5.3) 使用阻塞队列(BlockingQueue)控制线程通信

生产者向BlockingQueue中添加元素,如果队列已满,则会阻塞。
消费者向BlockingQueue中消费元素,如果队列已空,则会阻塞。

抛出异常 不同返回值 阻塞线程 指定超时时长
队尾插入元素 add(e) offer(e) put(e) offer(e, time,unit)
队头删除元素 remove(e) poll take() poll(time,unit)
获取,不删除元素 element() peek()
public class BlockQueue {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(2);
        blockingQueue.put("java");
        blockingQueue.put("java");
        blockingQueue.put("java");
        System.out.println(blockingQueue.size());
    }
}
public class BlockQueue {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> blockingQueue = new LinkedBlockingDeque<>(1);
        new Producer(blockingQueue).start();
        new Producer(blockingQueue).start();
        new Producer(blockingQueue).start();
        new Consumer(blockingQueue).start();
    }
}

class Producer extends Thread {
    private BlockingQueue<String> blockingQueue;

    public Producer(BlockingQueue<String> blockingQueue) {
        this.blockingQueue = blockingQueue;
    }

    @Override
    public void run() {
        String[] strings = new String[]{
                "java",
                "Structs",
                "Spring",
        };
        for (int i = 0; i < 100; i++) {
            System.out.println(getName() + "生产者准备生产");
            try {
                Thread.sleep(200);
                blockingQueue.put(strings[i % 3]);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(getName() + "生产完成" + blockingQueue);
        }
    }
}

class Consumer extends Thread {
    private BlockingQueue<String> blockingQueue;

    public Consumer(BlockingQueue<String> blockingQueue) {
        this.blockingQueue = blockingQueue;
    }

    @Override
    public void run() {
        while (true) {
            System.out.println(getName() + "消费者准备消费集合元素");
            try {
                Thread.sleep(200);
                blockingQueue.take();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(getName() + "消费完成:" + blockingQueue);
        }
    }
}

10.6)线程组

线程组可以对一批线程进行分类管理,Java允许对线程组进行控制。如果没有显示指定线程属于哪个线程组,则该线程组属于默认线程组。子线程和创建它的父线程处于同一个线程组。一旦加入一个线程组,就一直属于这个线程组。

创建线程时指定线程组

Thread(ThreadGroup group, Runnable target)
Thread(ThreadGroup group, Runnable target, String name) 
Thread(ThreadGroup group, String name)

设置了线程组后中途不能改变。只有getThreadGroup()没有setThreadGroup()
创建线程组

// 指定线程组的名字
ThreadGroup(String name) 
// 指定父线程组和线程组的名字
ThreadGroup(ThreadGroup parent, String name)

线程组默认实现了线程异常处理类接口,当有子线程没有处理异常,则线程组会捕获该异常来处理。

ThreadGroup类提供了几个常用方法来操作整个线程组里的所有线程。

int activeCount(); // 活动线程数
interrupt(); //  中断线程
isDaemon(); // 判断线程组是否是后台线程
setDaemon(boolean daemon); // 设置为后台线程
setMaxPriority(int pri); // 设置线程组的最高优先级

10.7) 线程池

10.7.1) 线程池原理和好处

线程出现的原因:
系统启动一个新线程的成本是比较高,因为涉及与操作系统的交互,所以使用线程池可以很好提高程序的性能,尤其是程序需要创建大量生存期很短的线程,更应该考虑线程池。

线程池的原理:
与数据库连接池类似,线程池在系统启动时创建大量的空闲线程,程序将Runnable对象 Callable对象传递给线程池,线程池就会启动一个线程来执行他们的run()或者call()方法,当run()或者call()方法执行结束后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个Runnable对象的run()或call()方法。

线程池的好处

10.7.2)线程池的创建

// 创建一个具有缓存功能线程池
Executors.newCachedThreadPool();

// 创建一个固定个数线程的线程池
Executors.newFixedThreadPool(4);
Executors.newSingleThreadExecutor();

// 创建指定个数的线程池,在指定延迟后执行线程任务
Executors.newScheduledThreadPool(6);
Executors.newSingleThreadScheduledExecutor();

// 创建持有足够的线程的线程池,会使用多个队列来减少竞争
Executors.newWorkStealingPool(4);
Executors.newWorkStealingPool();

前三个方法返回一个ExecutorService对象,该对象是一个线程池,可以执行Runnable对象和Callable对象。后两个返回ScheduledExecutorService。

ExecutorService尽快执行线程的线程池。
可执行的方法

// 无返回值
Future<?> submit(Runnable task)
// 用result显示指定返回的结果,所以返回result
<T> Future<T> submit(Runnable task,T result) 
ExecutorService executorService = Executors.newFixedThreadPool(6);
    Runnable targer = () -> {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "的值为" + i);
        }
    };
    executorService.submit(targer);
    executorService.submit(targer);
    executorService.submit(targer);
    executorService.shutdown();

用完一个线程池后,应该调用该线程池的shutdown()方法,该方法将启动线程池的关闭序列,调用shutdown()后不再接受新任务。

10.7.3)增强的ForkJoinPool

ForkJoinPool支持多CPU,是ExecutorService的实现类。

常用的构造器

// 创建10个并行线程的线程池 10个是CPU个数
ForkJoinPool forkJoinPool = new ForkJoinPool(10);

// 可用CPU个数作为参数
ForkJoinPool forkJoinPool = new ForkJoinPool(Runtime.getRuntime().availableProcessors());

// 返回一个通用线程池,通用池的运行状态不受shutdown()或shutdownNow()影响
ForkJoinPool forkJoinPool = ForkJoinPool.commonPool();

// 返回通用池的并行级别
int commonPoolParallelism = ForkJoinPool.getCommonPoolParallelism();

创建ForkJoinPool之后就可以调用submit(ForkJoinTask task)或invoke(ForkJoinTask task)方法来执行指定的任务。

ForkJoinTask代表一个可以并行,合并的任务。他的两个抽象子类 RecursiveAction和RecursiveTask。其中RecursiveTask代表有返回值的任务,RecursiveAction没有返回值的任务。

public class ForkJoinPoolThread {
    public static void main(String[] args) {
        ForkJoinPool pool = new ForkJoinPool();
        pool.submit(new PrintTask(0, 300));
        try {
            pool.awaitTermination(2, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        pool.shutdown();
    }
}

class PrintTask extends RecursiveAction {

    // 每个小任务最多只能打印50个数
    private static final int THRESHOLD = 50;
    private int start;
    private int end;

    public PrintTask(int start, int end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected void compute() {
        if (end - start < THRESHOLD) {
            for (int i = start; i < end; i++) {
                System.out.println(Thread.currentThread().getName() + "的i值:" + i);
            }
        } else {
            // 将大任务分解
            int middle = (start + end) / 2;
            PrintTask left = new PrintTask(start, middle);
            PrintTask right = new PrintTask(middle, end);
            
            // 并行执行多个小任务
            left.fork();
            right.fork();
        }
    }
}
public class ForkJoinPoolThread {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        int arr[] = new int[100];
        Random random = new Random();
        int total = 0;
        for (int i = 0, len = arr.length; i < len; i++) {
            int tmp = random.nextInt(20);
            total += (arr[i] = tmp);
        }
        System.out.println(total);
        ForkJoinPool forkJoinPool = ForkJoinPool.commonPool();
        Future<Integer> future = forkJoinPool.submit(new CalTask(arr, 0, arr.length));
        System.out.println(future.get());
    }
}

class CalTask extends RecursiveTask<Integer> {
    private static final int THRESHOLD = 20;
    private int arr[];
    private int start;
    private int end;

    public CalTask(int[] arr, int start, int end) {
        this.arr = arr;
        this.start = start;
        this.end = end;
    }

    @Override
    protected Integer compute() {
        int sum = 0;
        if (end - start < THRESHOLD) {
            for (int i = start; i < end; i++) {
                sum += arr[i];
            }
            return sum;
        } else {
            int middle = (start + end) / 2;
            CalTask left = new CalTask(arr, start, middle);
            CalTask right = new CalTask(arr, middle, end);
            // 并行执行小任务
            left.fork();
            right.fork();
            // 将小任务的结果合并起来
            return left.join() + right.join();
        }
    }
}

10.7.4)THreadPoolExecutor(重点)

Executor框架中最核心的成员是ThreadPoolExecutor。他是线程池的核心实现类。

mThreadPoolExecutor = new ThreadPoolExecutor(
        int corePoolSize,
        int maximumPoolSize,
        long keepAliveTime,
        TimeUnit unit,
        BlockingQueue<Runable> workQueue
        ThreadFactory threadFactory,
                RejectedExecutorHandler handler);

mThreadPoolExecutor.allowCoreThreadTimeOut(true);

①int coreSize : 核心线程数(不会被回收)。

②int maxSize : 最大线程数。

③long KeepAliveTime : 膨胀出来的线程回收时间。

④TimeUnit unit : 时间单位。(通过TimeUnit.时间单位调用)

⑤BlockingQueue queue : 线程的阻塞队列。(必须有界)

⑥ThreadFactory factory : 产生线程的工厂。

⑦RejectedExecutionHandler handler : 当线程大于总数(最大线程数 + 阻塞队列)时,将由handler拒绝任务。

![image-20190811143946990](/Users/wangbo/Library/Application Support/typora-user-images/image-20190811143946990.png)

![image-20190812173842577](/Users/wangbo/Library/Application Support/typora-user-images/image-20190812173842577.png)

如果我们执行ThreadPoolExecutor的execute方法,会遇到各种情况:

(1) 如果线程池中的线程数未达到核心线程数,则创建核心线程处理任务。
(2)如果线程数大于或者等于核心线程数,则将任务加入任务队列,线程池中的空闲线程会不断地从任务队列中取出任务进行处理。
(3)如果任务队列满了,并且线程数没有达到最大线程数,则创建非核心线程去处理任务。
(4)如果线程数超过了最大线程数,则执行饱和策略。

例子

package com.cari.cari.promo.diskon.util;

import android.os.Handler;
import android.os.Looper;

import com.cari.promo.diskon.BuildConfig;
import com.crashlytics.android.Crashlytics;

import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public enum ThreadUtil {
    Database(2),
    General(4),
    Network(6);

    private static final Handler MAIN_HANDLER = new Handler(Looper.getMainLooper());

    public static boolean checkIsInMainThread() {
        return Looper.myLooper() == Looper.getMainLooper();
    }

    public static void confirmInMainThread() {
        if (BuildConfig.DEBUG && (!checkIsInMainThread())) {
            throw new RuntimeException("Confirm Ui Thread Error!");
        }
    }

    public static void runInMainThread(boolean alwaysPost, Runnable runnable) {
        if (runnable == null) {
            return;
        }

        if ((!alwaysPost) && checkIsInMainThread()) {
            runnable.run();
        } else {
            MAIN_HANDLER.post(runnable);
        }
    }

    public static void runInMainThreadDelayed(Runnable runnable, long delayMillis) {
        if (runnable == null) {
            return;
        }

        MAIN_HANDLER.postDelayed(
                runnable,
                delayMillis < 0 ? 0 : delayMillis);
    }

    private final ThreadPoolExecutor mThreadPoolExecutor;

    ThreadUtil(int poolSize) {
        if (poolSize < 1) {
            poolSize = 1;
        }

        mThreadPoolExecutor = new ThreadPoolExecutor(
                poolSize,
                poolSize,
                30,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(),
                new ThreadFactory() {
                    @Override
                    public Thread newThread(Runnable r) {
                        return new Thread(r, "ThreadUtil-" + name());
                    }
                });

        mThreadPoolExecutor.allowCoreThreadTimeOut(true);
    }

    public Future<?> execute(Runnable runnable) {
        return mThreadPoolExecutor.submit(new RunnableTask(runnable));
    }

    private static final class RunnableTask implements Runnable {
        private final Runnable mRunnable;

        private RunnableTask(Runnable runnable) {
            mRunnable = runnable;
        }

        @Override
        public void run() {
            try {
                if (mRunnable != null) {
                    mRunnable.run();
                }
            } catch (Throwable t) {
                if (checkIsInMainThread()) {
                    throw t;
                } else {
                    final RuntimeException exception = new RuntimeException("Sub-Thread throws exception!", t);
                    if (BuildConfig.DEBUG) {
                        // 让主线程也崩溃
                        runInMainThread(false, new Runnable() {
                            @Override
                            public void run() {
                                throw exception;
                            }
                        });
                    } else {
                        // 记录到统计平台
                        runInMainThread(false, new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    Crashlytics.logException(exception);
                                } catch (Throwable ignore) {
                                }
                            }
                        });
                    }
                }
            }
        }
    }
}

10.8 线程池的种类

通过直接或间接配置ThreadPoolExecutor的参数可以创建不同类型的ThreadPoolExecutor。

4种线程池比较常用、FixedThreadPool CachedThreadPool SingleThreadExecutor ScheduledThreadPool

这几个线程池都是Executors 类中的静态方法生成的。

10.8.1)FixedThreadPool

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }

10.8.2)CachedThreadPool

 public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }

10.8.3) SingleThreadExecutor

 public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }

10.8.4) ScheduleThreadPool

 public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
    }

10.9)线程相关类

ThreadLocal类

ThreadLocal的方法

// 放回当前线程中副本中的值
T get()
// 删除此线程局部变量中当前线程的值
void remove()
// 设置次线程局部变量中当前副本的值
void set(T value)
public class ThreadLocalVar extends Thread {
    private Acount acount;

    public ThreadLocalVar(Acount acount, String name) {
        super(name);
        this.acount = acount;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            if (i == 6) {
                acount.setName(getName());
            }
            System.out.println(acount.getName() + "账户I的值" + i);
        }
    }

    public static void main(String[] args) {
        Acount acount = new Acount("初始名");
        new ThreadLocalVar(acount, "线程A").start();
        new ThreadLocalVar(acount, "线程B").start();
    }
}

class Acount {
    private ThreadLocal<String> name = new ThreadLocal<>();

    public Acount(String str) {
        this.name.set(str);
        // 访问当前线程的name的副本
        System.out.println(this.name.get());
    }

    public String getName() {
        return this.name.get();
    }

    public void setName(String str) {
        this.name.set(str);
    }
}

11、类加载和反射

11.1) 类加载、链接和初始化

11.1.1)JVM和类

当调用java命令运行程序时,会启动一个JVM,同一个JVM下的所有线程,变量都处于同一个进程中,都使用该JVM进程内存区。不同JVM不共享内存区。

JVM终止的情况

11.1.2)类加载

第一次使用某个未被加载到内存中的类时,系统会通过加载、连接、初始化三个步骤对类进行初始化。

类加载: 是指将类的class文件读入内存,并为之创建一个java.lang.Class对象。

类的加载由加载器完成,可以自定义加载器。

11.1.3)类的连接

连接负责把类的二进制数据合并到JRE中。

11.1.4)类的初始化

虚拟机对类进行初始化。

  1. 声明类变量时指定的初始化值
  2. 静态初始化块为类变量指定初始值
public class Test {
    // 声明时初始化
    static int a = 4;
    // 默认值
    static int b;
    static int c;

    // 静态初始化块
    static {
        b = 6;
    }
}

类的初始化步骤:

  1. 如果类没有被加载和连接,则程序先加载并连接该类
  2. 如果该类的直接父类还没有初始化,则先初始化其直接父类
  3. 如果类中有初始化语句,则系统一次执行这些初始化语句。

11.1.5)类的初始化时机

  1. 创建类的实例。
  2. 调用某个类的类方法。
  3. 访问某个类或接口的类变量,或为该类变量赋值。
  4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。
  5. 初始化某个累的子类。
  6. 直接使用java.exe命令来运行某个主类。

类变量使用final修饰,它的值在编译时就能确定,在使用该变量时相当于宏替换。

ClassLoader classLoader = ClassLoader.getSystemClassLoader();
try {
    classLoader.loadClass("com.company.baseLib.Tester");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}
System.out.println("系统加载Tester类");
try {
    Class.forName("com.company.baseLib.Tester");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

loadClass只是加载并没有初始化,forName做了初始化。

11.2)类加载器

类加载器负责将.class文件加载到内存中,并为之生成对应的java.lang.Class对象。同一个类不会被加载两次。JVM中的类有唯一的标识。全限定类名和加载器负责加载。不同加载器加载同类名是不同的标识。

11.3)反射查看类信息

编译时类型和运行时类型。

Person p = new Student();
// 编译时 Person,运行时Student。

11.3.1)获得Class对象

// 1. Class累的forName()静态方法
Class.forName("com.company.baseLib.Tester");
// 2. 类的class属性
Tester.class;
// 3. 对象的getClass()方法
Tester tester = new Tester();
tester.getClass();

推荐用第二种方式获取Class对象。

11.3.2)从Class中获取信息

![image-20190713110505792](/Users/wangbo/Library/Application Support/typora-user-images/image-20190713110505792.png)

getPackage();
getName()
getSuperclass()
getInterfaces()
上一篇 下一篇

猜你喜欢

热点阅读