javase学习

常用的DOS命令

cd
dir:查看目录
盘符
ipconfig
del 文件
ping

java语言的特性

  • 简单性
  • 面向对象
  • 健壮性
  • 多线程
  • 可移植性

java程序设计的核心

  1. 高内聚:类的内部数据操作细节自己完成,不允许外部干涉
  2. 低耦合:仅暴露少量的方法供外界使用

JDK、JRE、JVM三者之间的关系

  • JDK:开发工具包
  • JRE:(运行环境)运行时类库
  • JVM:java虚拟机

范围从小到大:

JVM -> JRE -> JDK

java编译和运行

# 假设需要编译的文件为 Hello.java
javac Hello.java
# 编译成功后会生成Hello.class文件

# 运行
java Hello

注释

// 单行注释

/*
	多行注释
 */

/**
 * 文档注释
 */

标识符与关键字

标识符的命名规则

标识符只能由数字、字母(可以是中文,但不推荐)、$、下划线(_)组成,首字母不能是数字。

可以标识

  • 类名
  • 方法名
  • 变量名
  • 接口名
  • ……

标识符的命名规范

见名知意

包名:首字母小写 + 驼峰命名

变量名:首字母小写 + 驼峰命名

方法名:首字母小写 + 驼峰命名

常量名:全大写,并且单词之间用"_"衔接

类名:首字母大写 + 驼峰命名

接口名:首字母大写 + 驼峰命名

变量

分类

  1. 基本类型
    1. 整数型(默认值:0)
      1. byte
      2. short
      3. int
      4. long(一般数值后面加 L 区分)
    2. 浮点型(默认值:0.0)
      1. float(一般数值后面加 F 区分)
      2. double
    3. char
    4. boolean(默认值:false)
      1. false
      2. true
  2. 引用类型
    1. 数组
    2. 接口

作用域

  • 类变量(加了static关键字)
  • 实例变量
  • 局部变量

常量

// 初始化后,不允许修改值。
final 数据类型 常量名 =;

运算符

  • 算术运算符:+、-、*、/、%、++、–

  • 赋值运算符:=

  • 关系运算符:>、<、>=、<=、==、!=、instanceof

  • 逻辑运算符:&&、||、!

  • 位运算符 教程:&、|、^、~、>>、<<、>>>

  • 条件运算符:? :

  • 扩展赋值运算符:+=、-=、*=、/=

包机制(package)

优点

  • 解决类名重名的问题
  • 为了方便程序的管理。不同功能的类分别存放在不同的包下

用法

package 包名1[.包名2.包名3....];
// import 必须放在 package下面
import package1[.package2.package3...].(classname|*);

命名规范

  • 一般采用公司域名倒序的方式。
  • 公司域名倒序 + 项目名 + 模块名 + 功能名

注意

  • package语句只允许出现在java源代码的第一行。

import

使用场景

A类中使用B类时

  • A类和B类在同一个包下,不需要使用import
  • A类和B类不在同一个包下,需要使用import

java.lang.*;这个包下的类不需要使用import导入

用法

import语句只能出现在package语句之下,class声明语句之上。
import语句还可以使用*号的方式。

java Doc

参数信息

  • @author 作者名
  • @version 版本号
  • @since 指明需要最早使用的jdk版本
  • @param 参数名
  • @return 返回值情况
  • @throws 异常抛出情况
# 文件为Doc.java
javadoc [-encoding UTF-8] [-charset UTF-8] Doc.java

进制

  1. 二进制 数值以"0b"开头
  2. 十进制
  3. 八进制 数值以"0"开头
  4. 十六进制 数值以"0x"开头

转义字符

\n、\t、

类型转换

规则

基本类型中,除了boolean无法和其他类型转换外,其余七种基本类型都可以相互转换。
byte、short、char混合运算,先各自转换成int再运算。
多种数据混合运算,各自先转换成容量大的一种再运算。

容量从小到大排序

​ byte < short(char) < int < long < float < double

分类

自动类型转换

强制类型转换

弊端
  • 可能出现精度损失

控制语句

if控制

switch控制

循环

for循环

while循环

do…while循环

增强for循环

面向对象

  • OOA:面向对象分析
  • OOD:面向对象设计
  • OOP:面向对象编程

特征

  1. 封装
  2. 继承
  3. 多态

类和对象

类:现实世界中的某些事物具有相同的特征提取出来的概念,是抽象的结果,类就是一个“模板“

对象:实际存在的个体。

封装

属性私有化,提供get方法、set方法

继承 Extends

多态

存在的三个必要条件

  • 存在继承关系
  • 子类重写父类的方法
  • 父类引用指向子类对象

static

所有static修饰的都是类相关的,类级别的。

所有static修饰的,都是采用“类名.”方式访问的。不需要对象的参与即可访问,不会发生空指针异常的情况。

静态变量存储在方法区

应用场景

  • 当所属的内容在所有的对象都一样时,可以认为是类的内容,可以设为static。

final

final可以修饰变量、方法、类等。

final表示最终的,不可变。

static final联合修饰的变量成为“常量”,常量名建议全部大写,单词之间用_衔接。

注意

  • final修饰的类无法被其它类继承
  • final修饰的方法无法被重写
  • final修饰的变量只能赋值一次

实例变量如果没有手动赋值的话,系统会默认赋值;但final修饰的实例变量,必须初始化,否则编译报错。

案例

package com.bz02.finalDemo;

public class Test01 {
    public static void main(String[] args) {

    }
}

class A {
    int a;
    // 没有初始化
//    final int b;
    // 实例变量是在构造方法执行中初始化(new的时候),所以final修饰的实例变量在构造方法内手动赋值也行
    final int c;

    public A() {
        this.c = 10;
    }
}

抽象类

抽象类:将类与类之间的共同特征(类的基础上)进一步抽象提取形成了抽象类,因为类本身是不存在的,所以抽象类无法创建对象。

抽象类无法实例化,无法创建对象(无法实例化)。但是抽象类有构造方法,供子类使用。

非抽象类继承抽象类必须将抽象方法实现。

final和abstract不能联合使用,这两个修饰符是对立的。

类到对象是实例化,对象到类是抽象。

语法

[修饰符列表] abstract class 类名 {
	类体
}

接口

集合

集合在java中本质上就是一个容器,是一个对象,也有内存地址。

集合中任何时候存储的都是“引用”不能存储基本数据类型,也不能直接存储对象,都是存储对象的内存地址。

java中不同的集合对应不同的数据结构。

所有的集合类和集合接口都在java.util包下。

分类

  • 单个方式存储元素,这类的超级父接口:java.util.Collection;
  • 以键值对方式存储元素,这类的超级父接口:java.util.Map;

集合框架图

集合框架图
备注:

  • 实线空心箭头:继承关系(泛化关系)(is a)
  • 虚线空心箭头:实现关系
  • 实线实心箭头:关联关系(has a)

数据结构

Collection接口

Collection接口的常用方法
方法名描述
boolean add(E e)添加元素
int size()获取元素个数,不是获取集合容量
void clear()清空元素
boolean contains(Object o)底层调用equals()进行判断,判断当前集合是否包含某个元素;如果包含返回true,否则返回false
boolean remove(Object o)删除元素中某个元素
boolean isEmpty()判断集合中元素的个数是否为0;如果个数为0返回true,否则返回false
Object[] toArray()调用这个方法可以把集合转换成数组
Iterator(迭代器)
注意

Iterator只有在Collection及子类中才能使用,Map集合不能使用。(具体查看集合框架图)

当集合的结构发生改变,迭代器必须重新获取,如果还是用老的迭代器,会出现异常:java.util.ConcurrentModificationException。

方法
方法名描述
boolean hasNext()如果仍有元素可以迭代,则返回true
E next()返回迭代的下一个元素
void remove()
案例
package com.bz.collectionDemo;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class IteratorTest01 {
    public static void main(String[] args) {
        /*
            Collection是接口,没法new(创建)
            Collection c = new ArrayList(); 多态的体现(1.继承关系 2.重写方法 3.父级引用指向子级对象)
         */
        // 创建Collection集合
        Collection c = new ArrayList();
        // 向集合添加数据
        c.add("第一条数据");
        c.add(1);
        c.add(new Object());

        /*
         方法一:不采用迭代器来实现
        // 集合转换为数组,才能进行循环
        Object[] obj = c.toArray();
        for(int i=0; i<obj.length; i++) {
            System.out.println(obj[i]);
        }
         */
        /*
            使用Iterator迭代器实现
         */
        // 获取迭代器
        Iterator it = c.iterator();

        // hasNext():是否还有下一条数据
        while(it.hasNext()) {
            Object obj = it.next(); // 控制迭代器指针下移,并读取下移后的记录
            System.out.println(obj);
        }
    }
}
List接口
特点

有序、有下标(下标从0开始)、数据可重复

List接口特有的方法
方法名备注
void add(int index, E element)在列表的指定位置插入指定元素(可选操作)。
E get(int index)返回列表中指定位置的元素。
int indexOf(Object o)返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。
int lastIndexOf(Object o)返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 -1。
E remove(int index)移除列表中指定位置的元素(可选操作)。
E set(int index, E element)用指定元素替换列表中指定位置的元素(可选操作)。
ArrayList集合(对象)

ArrayList集合底层为Object类型的数组,非线程安全。

ArrayList集合默认初始化容量为10(底层先创建了一个长度为0的数组,在添加第一个元素时,初始化容量为10)。

扩容到原容量的1.5倍。

可以使用java.util.Collections(工具类)把ArrayList集合非线程安全转换成线程安全。

案例
package com.bz.collectionDemo;

import java.util.ArrayList;
import java.util.List;

public class ArrayListTest01 {
    public static void main(String[] args) {
        // list1默认容量为10(底层先创建了一个长度为0的数组,在添加第一个元素时,初始化容量为10)
        // list1长度为0
        List list1 = new ArrayList();
        System.out.println(list1.size()); // 结果:0

        // list2容量为20
        // list2长度为0
        List list2 = new ArrayList(20);
        System.out.println(list2.size()); // 结果:0

        /*
            注意:
            size()获取的是集合中元素的个数,并非获取集合的容量
         */
    }
}
package com.bz.collectionDemo;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * ArrayList集合变成线程安全的
 */
public class ArrayListTest02 {
    public static void main(String[] args) {
        List list = new ArrayList(); // 非线程安全的

        // 变成线程安全
        Collections.synchronizedList(list);


        list.add("abc");
        list.add("efg");
        list.add("xyz");

        for(int i=0; i<list.size(); i++) {
            System.out.println(list.get(i));
        }
    }
}
优点
  • 【检索功能发挥到极致】检索效率高(每个元素占用的空间大小相同,内存地址连续,知道首元素的内存地址,然后知道下标,就可以通过数学表达式计算出内存地址,所以效率高)
缺点
  • 随机增删元素效率比较低(向数组末尾追加元素效率高)
  • 数组无法存储大数据量(很难找到一块很大的连续的内存空间)
LinkedList集合(对象)

LinkedList集合底层采用了双向链表数据结构

LinkedList集合不会默认初始化容量。

元素在空间存储,内存地址不连续。

优点
  • 【把随机增删功能发挥到极致】随机增删元素效率较高(因为内存地址不是连续的,所以增删时不涉及到大量元素位移)
缺点
  • 查询效率较低,每次查找某个元素时都需要从头节点开始往下遍历。
单向链表

基本单元是节点Node

节点Node有两个属性

  1. 数据
  2. 下个节点Node的内存地址
双向链表

基本单元也是节点Node

节点Node有三个属性

  1. 上个节点Node的内存地址
  2. 数据
  3. 下个节点Node的内存地址
链表优点
  • 随机增删元素效率较高(因为内存地址不是连续的,所以增删时不涉及到大量元素位移)
链表缺点
  • 查询效率较低,每次查找某个元素时都需要从头节点开始往下遍历。
Vector集合(对象)

Vector集合底层采用了数组这种数据结构

Vector集合中所有的方法都是线程同步的,线程安全,但效率较低,使用较少。

初始容量为10,扩容之后是原容量2倍。

ArrayList集合和Vector集合的区别
相同点

ArrayList集合和Vector集合的底层都是采用数组这种数据结构。

不同点

ArrayList集合是非线程安全效率较高,可以使用java.util.Collections(工具类)把ArrayList集合非线程安全转换成线程安全。

Vector集合是线程安全效率较低,现在有其它的方案代替,所以使用较少

Set接口
特点

无序、无下标、不可重复

HashSet集合(对象)

HashSet集合底层采用了哈希表数据结构。

HashSet集合在new时,实际上底层是new HashMap集合。言外之意:向HashSet存储数据时,实际上是存储到HashMap中。

HashSet集合元素也需要重写hashCode()和equals();【原因在HashMap出查看】

package com.bz.collectionDemo;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/**
 * HashSet用法:无序、不可重复
 */
public class HashSetTest01 {
    public static void main(String[] args) {
        // 创建集合
        Set<Integer> set = new HashSet<>();

        // 往集合添加元素
        set.add(1);
        set.add(100);
        set.add(40);
        set.add(20);
        set.add(40);

        // 迭代器
        Iterator<Integer> it = set.iterator();
        while(it.hasNext()) {
            Integer num = it.next();
            System.out.print(num + "\t");
        }
        // 结果:1	100	20	40	
    }
}
SortedSet接口

因为继承的是Set接口,所以其特点也是无序、无下标、不可重复。但放在SortedSet集合中的元素(非自定义类型)可以自动排序,我们称为可排序集合。放到该集合中的数据会自动按照大小顺序排序。

TreeSet集合(对象)

TreeSet集合底层采用了二叉树数据结构。

TreeSet集合在new时,实际上底层是new TreeMap集合。言外之意:向TreeSet存储数据时,实际上是存储到TreeMap中。

package com.bz.collectionDemo;

import java.util.Set;
import java.util.TreeSet;

/**
 * TreeSet:无序、不可重复,但会从小到大自动排序
 */
public class TreeSetTest01 {
    public static void main(String[] args) {
        Set<Integer> set = new TreeSet<>();
        set.add(5);
        set.add(10);
        set.add(30);
        set.add(15);
        set.add(4);
        set.add(15);
        set.add(24);

        for(Integer num : set) {
            System.out.print(num + "\t");
        }

        // 结果:4	5	10	15	24	30
    }
}

Map接口

  • Map集合和Collection集合没有关系
  • Map集合以Key/value这种键值对的方式存储元素
  • key和value都是引用类型,存储java对象的内存地址
  • key起到主导地位,value是key的一个附属品
  • 所有Map集合的key特点:无序、不可重复
  • Map集合的key和Set集合存储元素的特点相同
Map接口的常用方法

教程地址

方法名说明
V put(K key, V value)将指定的值与此映射中的指定键关联(可选操作)。
V get(Object key)返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。
void clear()从此映射中移除所有映射关系(可选操作)。
boolean containsKey(Object key)如果此映射包含指定键的映射关系,则返回 true
boolean containsValue(Object value)如果此映射将一个或多个键映射到指定值,则返回 true
boolean isEmpty()如果此映射未包含键-值映射关系,则返回 true。
判断集合中元素个数是否为0,为0返回true
Set keySet()返回此映射中包含的键的 Set 视图。
获取Map集合中所有的key
V remove(Object key)如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。
int size()返回此映射中的键-值映射关系数。
Collection<V> values()返回此映射中包含的值的 Collection 视图。
Set<Map.Entry<K,V>> entrySet()返回此映射中包含的映射关系的 Set 视图。
HashMap集合(类)

底层是哈希表(散列表)数据结构,非线程安全

HashMap集合的默认初始化容量为16,默认加载因子为0.75(即当集合中的元素个数达到集合的总容量的75%时,数组开始扩容)

HashMap集合允许key、value允许为null


哈希表:

  • 数组和单向链表的结合体

  • 查询、随机增删方面效率高

put(k, v)和get(k)方法通过查看底层逻辑发现:k调用了hashCode()和equals()方法。所以这两个方法需要重写*(原因:官方提供的是比较内存地址,而我们需要的是比较内容)*。

注意:

​ 同一个单向链表上所有节点的哈希值是相同,因为他们的数组下标是一样的;单同个链表上k和k的equals都是不同的。

​ HashMap集合初始化容量必须还是2的倍数,这个是官方推荐的,因为这样达到散列均匀,为了提高HashMap集合的存放效率,这个是所必须的。

重点:

​ 放在HashMap集合key部分的元素,以及放在HashSet集合的元素,需要同时重写hashCode方法 和 equals方法。

​ HashMap集合的key,null值只能有一个

Hashtable集合(类)

底层是哈希表数据结构,线程安全效率低现在很少用,因为有其它方案替代

Hashtable集合的key、value都不允许为null。

Hashtable集合的默认初始化容量为11,默认加载因子为0.75f

Hashtable集合的扩容: 原容量 * 2 + 1

Properties(类)【708集】

Properties是一个Map集合,继承Hashtable

Properties被称为“属性类

Properties集合是线程安全的,存储元素的时候也是key/value形式,并且key和value只支持String类型

常用方法

方法名备注
Object setProperty(String key, String value)底层调用Hashtable的put方法
String setProperty(String key)用指定的键在此属性列表中搜索属性
SortedMap接口

SortedMap集合的特点是无序不可重复。但放在SortedMap集合中元素的key部分可以自动排序,我们称为可排序集合。放到该集合中数据的key部分会自动按照大小顺序排序。

TreeMap集合

底层采用了二叉树数据结构。

所有实现类的总结

  • ArrayList:底层是数组,非线程安全,效率较高
  • LinkedList:底层是双向链表
  • Vector:底层是数组,线程安全,效率较低,使用较少
  • HashSet:底层是HashMap,放到HashSet集合中的元素等同于放到HashMap集合key部分。
  • TreeSet:底层是TreeMap,放到TreeSet集合中的元素等同于放到TreeMap集合key部分。
  • HashMap:底层是哈希表,非线程安全,效率较高
  • HashTable:底层是哈希表,线程安全,效率较低,使用较少
  • Properties:线程安全,key和value只能存储字符串(String),又称“属性类”
  • TreeMap:底层二叉表,TreeMap集合的key可以自动按照大小顺序排序
List集合存储元素的特点
  • 有序可重复
Set(Map)集合存储元素的特点
  • 无序不可重复
SortedSet(SortedMap)集合存储元素的特点
  • 无序不可重复,但是SortedSet(SortedMap)集合中的元素是可排序的(可以按照大小顺序自动排序)。

泛型

视频教程

泛型这种语法机制,只在编译阶段起作用,只是给编译器参考的(运行阶段泛型没用!)

好处
  • 集合中存储的元素类型统一
  • 从集合中取出的类型是泛型指定的类型,不需要大量的”向下转型“
缺点
  • 导致集合中存储的元素类型缺乏多样性
案例1

测试类:

package com.bz.collectionDemo.fanxingdemo;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * 泛型的使用
 * jdk5.0之后推出泛型机制
 * 泛型这种语法机制,只在编译阶段起作用,只是给编译器参考的(运行阶段泛型没用!)
 */
public class Test01 {
    public static void main(String[] args) {
        /*
            没有使用泛型时的做法,存在哪些缺陷


        // 创建一个集合
        List list = new ArrayList();
        // 创建一只猫
        Cat cat = new Cat();
        // 创建一只鸟
        Bird bird = new Bird();
        // 把猫添加到集合中
        list.add(cat);
        // 把鸟添加到集合中
        list.add(bird);

        // 创建迭代器
        Iterator iterator = list.iterator();
        // 循环
        while(iterator.hasNext()) {
            Object obj = iterator.next();
            if(obj instanceof Animal) {
                Animal animal = (Animal) obj;
                animal.Move();
            }
        }
        */

        // jdk5新增了泛型机制
        // 使用泛型List<Animal>之后,表示List集合中只允许存储Animal类型的数据
        // 用泛型来指定集合中存储的数据类型
        // 用了泛型后,集合中元素的数据类型更统一了。
        List<Animal> list = new ArrayList<Animal>();
        /*
            指定list集合只能存储Animal数据,那么存储String类型的数据,就会编译报错:
                Required type: Animal
                Provided: String
            list.add("a");
         */
        Cat cat = new Cat();
        Bird bird = new Bird();
        list.add(cat);
        list.add(bird);

        // 获取迭代器
        // 这个表示迭代器迭代的是Animal类型
        Iterator<Animal> iterator = list.iterator();
        while(iterator.hasNext()) {
            // 使用泛型后,每一次迭代返回的数据都是Animal类型
            Animal a = iterator.next();
            // 这里不需要强制类型转换,直接调用
            a.move();

            // 如果调用子类特有的方法还是需要”向下转型“
            if(a instanceof Cat) {
                Cat cat2 = (Cat)a;
                cat2.catchMouse();
            }
            if (a instanceof Bird) {
                Bird bird2 = (Bird)a;
                bird2.fly();
            }
        }
    }
}

Animal类:

package com.bz.collectionDemo.fanxingdemo;

/**
 * 动物类
 */
public class Animal {
    // 父类自带方法
    public void move() {
        System.out.println("动物在移动");
    }
}

Cat类:

package com.bz.collectionDemo.fanxingdemo;

/**
 * 猫类
 */
public class Cat extends Animal {
    // 特有方法
    public void catchMouse() {
        System.out.println("猫抓老鼠!");
    }
}

Bird类:

package com.bz.collectionDemo.fanxingdemo;

/**
 * 鸟类
 */
public class Bird extends Animal {
    // 特有方法
    public void fly() {
        System.out.println("鸟在飞!!!");
    }
}
案例2

其他类用案例1的代码,测试类:

package com.bz.collectionDemo.fanxingdemo;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * jdk8之后引入了:自动类型推断机制(又称:钻石表达式)
 */
public class Test02 {
    public static void main(String[] args) {
        // ArrayList<这里的类型会自动推断>()
        // 自动类型推断
        List<Animal> list = new ArrayList<>();
        // 创建对象
        Cat c = new Cat();
        Bird b = new Bird();
        // 往集合添加元素
        list.add(c);
        list.add(b);

        // 创建迭代器
        Iterator<Animal> it = list.iterator();
        if(it.hasNext()) {
            Animal a2 = it.next();
            a2.move();
        }
    }
}
自定义泛型案例
package com.bz.collectionDemo.fanxingdemo;

/**
 * 自定义泛型
 * @param 泛型名称 <标识符可以随便写>,一般用E(Element)或T(Type)
 */
public class DiyFanXiangTest01<标识符可以随便写> {
    public void doSome(标识符可以随便写 o) {
        System.out.println(o);
    }

    public static void main(String[] args) {
        // 创建对象指定了泛型是:String类型
        DiyFanXiangTest01<String> diyfx = new DiyFanXiangTest01<>();
        // doSome(String)实参类型只能为String类型,不是String类型会报错
        diyfx.doSome("泛型");

        DiyFanXiangTest01<Integer> diyfx2 = new DiyFanXiangTest01<Integer>();
        diyfx2.doSome(1);
    }
}

增强for循环(foreach)

package com.bz.arrayDemo;

/**
 * jdk5.0之后推出:增强for循环(又称foreach)
 *
 */
public class ForEachTest01 {
    public static void main(String[] args) {
        // 创建数组
        int[] arr = {2, 3, 5, 1, 10, 7, 2};

        /*
            普通循环

        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + "\t");
        }
         */

        /*
            增强for循环语法
            for(数据类型 变量名 : 数组或集合) {
                ……
            }
            缺点:
                没有下标
            在需要使用下标的循环中,不建议使用增强for循环
         */
        for(int num : arr) {
            System.out.print(num + "\t");
        }
    }
}
package com.bz.arrayDemo;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * 集合使用增强for循环(foreach)
 */
public class ForEachTest02 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("abc");
        list.add("efg");
        list.add("xyz");

        // 使用迭代器方式
        System.out.println("************* 使用迭代器方式 **************");
        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            String str = it.next();
            System.out.print(str + "\t");
        }

        System.out.println();
        System.out.println();

        System.out.println("************* 使用foreach方式 **************");

        // 循环
        for(String str : list) {
            System.out.print(str + "\t");
        }
    }
}

注意点

  • 存放在一个集合(Collection)中的类型,一定要重写contains()、remove()等,具体看源码。
package com.bz.collectionDemo;

import java.util.ArrayList;
import java.util.Collection;

/**
 * 测试contains方法、remove方法的使用及注意点
 */
public class ContainsTest01 {
    public static void main(String[] args) {
        Collection c = new ArrayList();

        User u1 = new User("张三");
        c.add(u1);

        String str1 = new String("abc");
        c.add(str1);


        User u2 = new User("张三");
        String str2 = new String("abc");
        System.out.println(c.contains(u2)); // User类没重写equals方法前结果:false 重写equals方法后结果为:true
        System.out.println(c.contains(str2)); // 结果:true 因为contains底层调用的是equals方法,但官方有对String类的equals方法进行过重写,改为内容比较

        // User类没重写equals方法前结果:删除不掉u2对象
        // User类没重写equals方法后结果:可以删除u2对象
        c.remove(u2);
        System.out.println(c.size());

    }
}

class User {
    String name;

    // 无参构造器
    public User() {

    }

    // 有参构造器
    public User(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object obj) {
        if(obj == null || !(obj instanceof User)) {
            return false;
        }
        if(this == obj) {
            return true;
        }
        User u = (User)obj;
        return this.name.equals(u.name);
    }
}

IO

  1. 定义

    I:input

    O: output

  2. 作用

    通过IO可以完成文件的读和写。

  3. IO流的分类

    1. 输入流
    2. 输出流
    3. 字节流
    4. 字符流

    1. 按照流的方向进行分类

      以内存作为参照物,往内存中去,叫做输入Input(读Read);从内存中出来,就做输出Output(写Write)

    2. 按照读取数据方式不同进行分类

      1. 字节流:有的流是按照字节的方式读取数据,一次读取1个字节(byte),等同于一次读取8个二进制位,这种流是万能的,什么类型的文件都可以读取,包括文本文件、图片、声音文件、视频文件……
      2. 字符流:有的流是按照字符的方式读取数据的,一次读取一个字符,这种流为了方便读取普通文本文件而存在的,这种流不能读取:图片、声音、视频等文件。只能读取纯文本文件,连word文件都无法读取。
  4. java中所有流都在:java.io.*下

  5. java IO流的四大家族:

    1. java.io.InputStream:字节输入流
    2. java.io.OutputStream:字节输出流
    3. java.io.Reader:字符输入流
    4. java.io.Writer:字符输出流

    注意:

    • 在java中只要类名以"Stream"结尾的都是字节流,以"Reader/Writer"结尾的都是字符流。
    • 四大家族的首领都是抽象类(abstract class)
  6. 所有的流都实现了

    1. 所有的流都实现了:java.io.Closeable接口,都是可关闭的,都有close()方法。流毕竟是一个管道,这个是内存和硬盘之间的通道,用完后一定要关闭,不然会占用很多资源。
  7. 所有的输出流都实现了

    1. 都实现了java.io.Flushable接口,都是可刷新的,都有flush()方法。养成一个好习惯,输出流在最终输出之后,一定要记得flush()刷新一下,这个刷新是将通道/管道当中剩余未输出的数据强行输出(清空管道)。刷新的作用就是清空管道。
    2. 注意:如果没有flush()可能会导致丢失数据。
  8. java.io包下需要掌握的流有16个:

    1. 文件专属
      1. java.io.FileInputStream
      2. java.io.FileOutputStream
      3. java.io.FileReader
      4. java.io.FileWriter
    2. 转换流(将字节流转换为字符流)
      1. java.io.InputStreamReader
      2. java.io.OutputStreamWriter
    3. 缓冲流专属
      1. java.io.BufferedReader
      2. java.io.BufferedWriter
      3. java.io.BufferedInputStream
      4. java.io.BufferedOutputStream
    4. 数据流专属
      1. java.io.DataInputStream
      2. java.io.DataOutputStream
    5. 标准输出流
      1. java.io.PrintWriter
      2. java.io.PrintStream
    6. 对象专属流
      1. java.io.ObjectInputStream
      2. java.io.ObjectOutputStream
  9. FileInputStream和FileOutputStream案例

    FileInputStream案例1

    package com.bz.javaio.demo01;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    
    /**
     * java.io.FileInputStream
     *      1.文件字节输入流,万能的,任何类型
     */
    public class FileInputStreamTest01 {
    
        public static void main(String[] args) {
            FileInputStream fis = null;
    
            try {
                // IDEA中工程Project的根就是IDEA的默认当前路径
                fis = new FileInputStream("src/com/bz/javaio/demo01/test");
    //            while(true) {
    //                int data = fis.read();
    //                if(data == -1) {
    //                    break;
    //                }
    //                System.out.println(data);
    //            }
                int data = 0;
                while ((data = fis.read()) != -1) {
                    System.out.println(data);
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if(null != fis) {
                    try {
                        fis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

    FileInputStream案例2

    package com.bz.javaio.demo01;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    
    /**
     * FileInputStreamTest01基础上优化
     */
    public class FileInputStreamTest02 {
        public static void main(String[] args) {
            FileInputStream fis = null;
    
            byte[] b = new byte[4];
    
            try {
                fis = new FileInputStream("src/com/bz/javaio/demo01/test");
    
    
    //            FileInputStream常用方法
                System.out.println("总共有:" + fis.available() + "个字节!\n");
                System.out.println("跳过一个字符\n");
                fis.skip(1);
    
    
    
                // Add exception to method signature:方法抛出异常
                int readCount = 0;
    
                while((readCount = fis.read(b)) != -1) {
                    String str = new String(b, 0, readCount);
                    /*
                        因为中文会被拆分,结果:
                            a中
                            b哈
                            哈�
                            ���
                            �哈
                     */
                    System.out.println(str);
                }
    
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if(fis != null) {
                    try {
                        fis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

    FileOutputStream案例1

    package com.bz.javaio.demo01;
    
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    /**
     * FileOutputStream练习1
     * 负责写:从内存到硬盘
     */
    public class FileOutputStreamTest01 {
        public static void main(String[] args) {
            FileOutputStream fos = null;
    
            try {
                // outputTest不存在时,会自动新建
                fos = new FileOutputStream("src/com/bz/javaio/demo01/test");
    
                // 开始写
                byte[] b = {97, 'b', 102, 'd'};
                fos.write(b);
    
                // 写完之后,最后一定要刷新
                fos.flush();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if(fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

    FileOutputStream案例2

    package com.bz.javaio.demo01;
    
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    /**
     * FileOutputStream内容追加测试
     */
    public class FileOutputStreamTest02 {
        public static void main(String[] args) {
            FileOutputStream fos = null;
    
            // outputTest不存在时,会自动新建, true:在文件末尾追加内容
            try {
                fos = new FileOutputStream("src/com/bz/javaio/demo01/test", true);
    
                byte[] byteData = {97, 98, 99, 100, 102};
                // 写入内容
                fos.write(byteData);
    
                String str = "哈!哈哈哈哈~~~";
                byte[] bytes = str.getBytes();
                fos.write(bytes);
    
                // 写完内容后记得刷新
                fos.flush();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if(fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

    FileInputStream、FileOutputStream结合使用

    package com.bz.javaio.demo01;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    /**
     * 使用FileInputStream + FileOutputStream 完成文件的拷贝
     * 拷贝的过程应该时一边读一边写
     * 使用以上的字节流拷贝文件的时候,文件类型随意,万能的。什么样的文件都可以拷贝。
     */
    public class CopyTest01 {
        public static void main(String[] args) {
            FileInputStream fis = null;
            FileOutputStream fos = null;
    
            try {
                // 创建一个输入流对象
                fis = new FileInputStream("src\\com\\bz\\javaio\\demo01\\fileInputTest.jpg");
                // 创建一个输出流对象
                fos = new FileOutputStream("src\\com\\bz\\javaio\\demo01\\jpgOutputTest.jpg");
    
                byte[] bytes = new byte[1024 * 1024]; // 1MB = 1024 * 1024 (1次最多拷贝1MB)
                int readCount = 0;
                while((readCount = fis.read(bytes)) != -1) {
                    fos.write(bytes, 0, readCount);
                }
                // 写入完成记得刷新一下
                fos.flush();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                // 分开try,不要一起try
                // 原因:一起try时,其中一个出异常,会影响后面的代码(流的关闭)执行。
                if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if(fis != null) {
                    try {
                        fis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
  10. FileReader和FileWriter案例

    FileReader案例

    package com.bz.javaio.demo02;
    
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.IOException;
    
    /**
     * FileRead使用:
     *  文件字符输入流,只能读取普通文本
     *  读取文本内容时,比较方便,快捷。
     */
    public class FileReaderTest01 {
        public static void main(String[] args) {
            FileReader fileReader = null;
            try {
                fileReader = new FileReader("src/com/bz/javaio/demo01/test");
    
                char[] chars = new char[4];
                int readCount = 0;
                while((readCount = fileReader.read(chars)) != -1) {
                    System.out.println(new String(chars, 0, readCount));
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (fileReader != null) {
                    try {
                        fileReader.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

    FileWriter案例

    package com.bz.javaio.demo02;
    
    import java.io.FileWriter;
    import java.io.IOException;
    
    /**
     * FileWrite使用
     */
    public class FileWriterTest01 {
        public static void main(String[] args) {
            FileWriter fw = null;
            try {
                // 每次都覆盖
    //            fw = new FileWriter("src/com/bz/javaio/demo01/test");
                // 在末尾追加
                fw = new FileWriter("src/com/bz/javaio/demo01/test", true);
    
                char[] chars = {'j', 'a', 'v', 'a', '修', '炼'};
    
                fw.write(chars);
    
                // 写入后需要刷新
                fw.flush();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (fw != null) {
                    try {
                        fw.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

    FileReader、FileWriter结合使用

    package com.bz.javaio.demo02;
    
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.FileWriter;
    import java.io.IOException;
    
    /**
     * FileReader、FileWriter结合使用
     *  只能拷贝普通文本文件
     */
    public class CopyTest01 {
        public static void main(String[] args) {
            // 创建一个字符流读取对象
            FileReader fr = null;
            // 创建一个字符流写入对象
            FileWriter fw = null;
    
            try {
                fr = new FileReader("src/com/bz/javaio/demo01/inputTest");
                fw = new FileWriter("src/com/bz/javaio/demo01/outputTest");
    
                char[] chars = new char[4];
                int readCount = 0;
    
                while((readCount = fr.read(chars)) != -1) {
                    // 写入
                    fw.write(chars, 0, readCount);
                }
                // 写入需要刷新
                fw.flush();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (fw != null) {
                    try {
                        fw.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (fr != null) {
                    try {
                        fr.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

File

File类和流的四大家族没有关系,所以File类不能完成文件的读和写操作。

File对象代表什么?

​ 文件和目录路径名的抽象表示形式。

File对象有可能对应的是目录,也有可能是文件。

常用方法

方法描述
boolean createNewFile()创建文件
boolean mkdir()创建单层目录
boolean mkdirs()创建多层目录
String getParent()获取父路径
File getParentFile()获取父路径
String getAbsolutePath()获取绝对路径
String getName()获取文件名
boolean isFile()判断是否是一个文件,true表示是一个文件
boolean isDirectory()判断是否是一个目录,true表示是一个目录
long lastModified()获取文件最后修改时间
long length()获取文件的大小
File[] listFiles()目录下所有的子文件/子目录列表

序列化与反序列化

序列化:Serialize,java对象存储到文件中,将java对象的状态保存下来的过程。

反序列化:DeSerialize,将硬盘上的数据重新恢复到内存当中,恢复成java对象。

ObjectOutputStream:序列化

ObjectInputStream:反序列化

参与序列化和反序列化的对象,必须实现Serializable接口,否则会报错:java.io.NotSerializableException(即:对象不支持序列化)。

注意:

​ 通过源代码发现,Serializable接口只是一个标志接口(接口当中什么代码也没有)


Serializable接口具体起到什么作用?

​ 起到标识的作用,标志的作用。java虚拟机看到这个类实现了这个接口,可能对这个类进行特殊待遇。

​ Serializable这个标志接口是给java虚拟机参考的,java虚拟机看到这个接口之后,会为该类自动生成一个序列化版本号。


序列化版本号的作用:

java.io.InvalidClassException: com.bz.javaio.demo06.User;
local class incompatible:
	stream classdesc serialVersionUID = 7212538078076707134, (新版本的序列化版本号)
	local class serialVersionUID = -8490852119502027634 (旧版本的序列化版本号)

java语言中是采用什么机制来区分类的?

  1. 首先通过类名进行比对,如果类名不一样,肯定不是同一个类。
  2. 如果类名一样,靠序列化版本号进行区分。

最终结论

凡是一个类实现了Serializable接口,建议给该类提供一个固定不变的序列化版本号。

这样,以后这个类即使代码修改了,但是版本号不改,java虚拟机会认为是同一个类。

案例:所有属性参与序列

Student.java文件

package com.bz.javaio.demo06;

import java.io.Serializable;

public class Student implements Serializable {
    
    // 建议把序列化版本号手动写出来,不建议自动生成
    private static final long serialVersionUID = 1L; // java虚拟机识别一个类的顺序:先通过类名;如果类名相同,再根据序列版本号
    
    private int no;

    private String name;

    public Student() {

    }

    public Student(int no, String name) {
        this.no = no;
        this.name = name;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

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

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

    @Override
    public String toString() {
        return "Student{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }
}

ObjectOutputStreamTest02.java文件

package com.bz.javaio.demo06;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * 序列化
 */
public class ObjectOutputStreamTest02 {
	public static void main(String[] args) {
		ObjectOutputStream oos = null;
		try {
			oos = new ObjectOutputStream(new FileOutputStream("ObjectOutputStreamTest02"));

			List<Student> list = new ArrayList<>();

			Student st1 = new Student(1, "zhangsan");
			Student st2 = new Student(2, "lisi");
			Student st3 = new Student(3, "wangwu");

			list.add(st1);
			list.add(st2);
			list.add(st3);

			oos.writeObject(list);

			// 写入完成需要刷新
			oos.flush();
            
            // 运行报错:java.io.NotSerializableException: com.bz.javaio.demo06.User
            // Student类无法被序列化
            // 解决方案:Student类需要继承Serializable接口
            
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (null != oos) {
				try {
					oos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

ObjectInputStreamTest02.java 文件

package com.bz.javaio.demo06;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.List;

/**
 * 集合
 * 反序列化
 */
public class ObjectInputStreamTest02 {
	public static void main(String[] args) {
		ObjectInputStream ois = null;
		try {
			ois = new ObjectInputStream(new FileInputStream("ObjectOutputStreamTest02"));

			List<Student> list = (List) ois.readObject();

			for(Student st : list) {
				System.out.println(st);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} finally {
			if (null != ois) {
				try {
					ois.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

运行结果:
在这里插入图片描述

案例:某个属性不参与序列化

如果对象中某个属性不参与序列化,添加transient关键字,如下:

Student.java文件

package com.bz.javaio.demo06;

import java.io.Serializable;

public class Student implements Serializable {
    
    // 建议把序列化版本号手动写出来,不建议自动生成
    private static final long serialVersionUID = 2L; // java虚拟机识别一个类的顺序:先通过类名;如果类名相同,再根据序列版本号

    
    private int no;

    // transient关键字表示游离的,不参与序列化
    private transient String name; // name不参与序列化操作

    public Student() {

    }

    public Student(int no, String name) {
        this.no = no;
        this.name = name;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

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

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

    @Override
    public String toString() {
        return "Student{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }
}

ObjectOutputStreamTest02.java文件

package com.bz.javaio.demo06;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * 序列化
 */
public class ObjectOutputStreamTest02 {
	public static void main(String[] args) {
		ObjectOutputStream oos = null;
		try {
			oos = new ObjectOutputStream(new FileOutputStream("ObjectOutputStreamTest02"));

			List<Student> list = new ArrayList<>();

			Student st1 = new Student(1, "zhangsan");
			Student st2 = new Student(2, "lisi");
			Student st3 = new Student(3, "wangwu");

			list.add(st1);
			list.add(st2);
			list.add(st3);

			oos.writeObject(list);

			// 写入完成需要刷新
			oos.flush();
            
            // 运行报错:java.io.NotSerializableException: com.bz.javaio.demo06.User
            // Student类无法被序列化
            // 解决方案:Student类需要继承Serializable接口
            
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (null != oos) {
				try {
					oos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

ObjectInputStreamTest02.java 文件

package com.bz.javaio.demo06;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.List;

/**
 * 集合
 * 反序列化
 */
public class ObjectInputStreamTest02 {
	public static void main(String[] args) {
		ObjectInputStream ois = null;
		try {
			ois = new ObjectInputStream(new FileInputStream("ObjectOutputStreamTest02"));

			List<Student> list = (List) ois.readObject();

			for(Student st : list) {
				System.out.println(st);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} finally {
			if (null != ois) {
				try {
					ois.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

运行结果:
在这里插入图片描述

IO + Properties 联合使用

配置文件

非常好的一个设计理念:

  • 以后经常改变的数据,可以单独写到一个文件中,使用程序动态读取,
  •  将来只需要修改这个文件的内容,java代码不需要改动,不需要重新
    
  •  编译,服务器也不需要重启。就可以拿到动态的信息。
    
  •  类似于以上机制的这种文件被称为:配置文件。
    

属性配置文件内容格式

key1=value
key2=value

java规范中有要求:属性配置文件建议以.properties结尾,但这不是必须的。

案例

userinfo.properties 文件

# 用户名
username=bz
# 密码
password=123

IoPropertiesTest01 文件

package com.bz.javaio.demo07;

import javax.annotation.processing.Filer;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;

/**
 * IO + Properties 联合使用
 * 非常好的一个设计理念:
 * 		以后经常改变的数据,可以单独写到一个文件中,使用程序动态读取,
 * 		将来只需要修改这个文件的内容,java代码不需要改动,不需要重新
 * 		编译,服务器也不需要重启。就可以拿到动态的信息。
 *
 * 	类似于以上机制的这种文件被称为:配置文件。
 * 	并且当配置文件中的内容格式是:
 * 		key1=value
 * 		key2=value
 * 	的时候,我们把这种配置文件叫做属性配置文件。
 *
 * 	java规范中有要求:属性配置文件建议以.properties结尾,但这不是必须的。
 */
public class IoPropertiesTest01 {
	public static void main(String[] args) {
		/**
		 * Properties是一个Map集合,key和value都是String类型
		 * 想将userinfo文件中的数据加载到Properties对象中
		 */
		FileReader fr = null;

		try {
			fr = new FileReader("src\\com\\bz\\javaio\\userinfo.properties");

			Properties pro = new Properties();
			pro.load(fr);

			String userName = pro.getProperty("username");
			System.out.println(userName);

			String password = pro.getProperty("password");
			System.out.println(password);

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (null != fr) {
				try {
					fr.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

	}
}

多线程

什么是进程?

进程是一个应用程序(1个进程是一个软件)

什么是线程?

线程是一个进程中的执行场景/执行单元。一个进程可以启动多个线程。

优点

提交效率

实现方式

  1. 编写一个类,直接继承java.lang.Thread,重写run方法

    package com.bz.threaddemo;
    
    /**
     * 实现线程的第一种方式:
     * 		编写一个类,继承java.lang.Thread,重写run方法
     *
     * 怎么创建线程对象?
     *		通过new对象
     *
     * 怎么启动线程?
     * 		调用线程对象的start()方法
     *
     * 注意:
     * 		亘古不变的的道理:
     * 			方法体中的代码永远都是自上而下的顺序依次逐行执行的。
     *
     * 以下程序的输出结果有这样的特点:
     * 		有先有后
     * 		有多有少
     */
    public class ThreadTest01 {
    	public static void main(String[] args) {
    		// 这里是main方法,这里的代码属于主线程,在主栈中运行
    
    		// 新建一个分支线程对象
    		AThread aThread = new AThread();
    		/*
    			start()方法的作用:
    				启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了。
    				这段代码的任务只是为了开启一个新的栈空间,只要新的栈空间开出来,start()方法就结束了,
    				线程就启动成功啦。
    
    			启动成功的线程会自动调用run()方法,并且run方法在分支栈的底部(压栈)
    			run方法在分支栈的栈底部,main方法在主栈的栈底部。run和main是平级的。
    		 */
    		// 启动线程
    //		aThread.run(); // 不会启动线程,不会分配新的分支栈
    		aThread.start();
    
    		// 这里的代码还是运行在主线程中
    		for (int i = 0; i < 1000; i++) {
    			System.out.println("主线程---> " + i);
    		}
    	}
    }
    
    class AThread extends Thread {
    	@Override
    	public void run() {
    		// 编写程序,这段程序运行在分支线程中(分支栈)
    		for (int i = 0; i <1000; i++) {
    			System.out.println("分支线程---> " + i);
    		}
    	}
    }
    
  2. 编写一个类,实现java.lang.Runnable接口,实现run方法

    package com.bz.threaddemo;
    
    /**
     * 实现线程的第二种方式:
     * 		编写一个类,实现java.lang.Runnable接口,实现run方法。
     */
    public class ThreadTest02 {
    	public static void main(String[] args) {
    		// 创建一个可运行的对象
    		BThread bt = new BThread();
    
    		// 将可运行的对象封装成一个线程对象
    		Thread thread = new Thread(bt);
    
    		// 启动线程
    		thread.start();
    
    		for (int i = 0; i < 1000; i++) {
    			System.out.println("主线程----> " + i);
    		}
    	}
    }
    
    class BThread implements Runnable {
    
    	@Override
    	public void run() {
    		for (int i = 0; i < 1000; i++) {
    			System.out.println("分支线程----> " + i);
    		}
    	}
    }
    

    注意:

    ​ 第二种方式实现接口比较常用,因为一个类实现了接口,它还可以去继承其它的类,更灵活。

生命周期

  1. 新建状态
  2. 就绪状态
  3. 运行状态
  4. 阻塞状态
  5. 死亡状态

守护线程

注意

  • 进程之间的内存独立不共享。
  • 在java语言中,线程之间堆内存和方法区内存共享。但是栈内存独立(一个线程一个栈)。栈与栈之间互不干扰。
  • 使用多线程之后,main方法结束,有可能程序还没结束;main方法结束只代表主线程结束了,主栈空了,其它的栈(线程)可能还在压栈弹栈。

线程安全

  1. 什么时候数据在多线程并发的环境下会存在安全问题?

    1. 多线程并发
    2. 有共享数据
    3. 共享数据有修改的行为
  2. 如何解决线程安全问题?

    线程排队执行(不能并发),这种机制被称为“线程同步机制”。

java中的三大变量

  1. 实例变量:在堆中
  2. 静态变量:在方法区
  3. 局部变量:在栈中

局部变量和常量 永远都不会存在线程安全问题,因为局部变量在栈中,永远都不会共享。(一个线程一个栈);

实例变量在堆中,堆只有1个;静态变量在方法区中,方法区只有1个。

堆和方法区都是多线程共享的,所以可能存在线程安全问题。

用法

  1. 同步代码块(灵活)

    synchronized (线程共享的对象) {

    ​ 同步代码块;

    }

  2. 在实例方法上使用synchronized,表示共享对象一定是this,并且同步代码块是整个方法体

  3. 在静态方法上使用synchronized,表示找类锁

    类锁永远只有1把,就算创建了100个对象,类锁都只有1把。

如何处理线程安全问题?

  1. 尽量使用局部变量代替“实例变量”和“静态变量”
  2. 如果必须是实例变量,那么可以考虑创建多个对象,这里实例变量的内存就不共享了。(1个线程对应1个对象,100个线程对应100个对象)
  3. 如果不能使用局部变量,对象也不能创建多个,这种情况就只能使用synchronized。线程同步机制。

反射机制

作用

  • 通过java语言中的反射机制可以操作字节码文件;通过反射机制可以操作代码片段(class文件)
  • 可以使程序变得更灵活

缺点

  • 打破封装

相关类在哪个包下?

java.lang.reflect.*;

相关的重要的类有哪些?

  • java.lang.Class:代表整个字节码,代表一个类型,代表整个类
  • java.lang.reflect.Method:代表字节码中的方法字节码,代表类中的方法
  • java.lang.reflect.Constructor:代表字节码中的构造方法字节码,代表类中的构造方法
  • java.lang.reflect.Field:代表字节码中的属性字节码,代表类中的成员变量(静态变量 + 实例变量)

java.lang.Class

如何获取实例
  1. Class c = Class.forName(“完整的类名”);

    说明:

    1. 静态方法
    2. 方法的参数是一个字符串
    3. 字符串需要的是一个完整类名
    4. 完整类名必须带有包名(类似:java.lang.String)
  2. Class c = 对象.getClass();

  3. java语言中任何一种类型(包括基本数据类型)都有.class属性。Class c = 任何类型.class;

注解(Annotation)

注解,或者叫做注释类型。

注解是一种引用数据类型。编译之后也是生成xxx.class文件。

定义语法

[修饰符列表] @interface 注解类型名 {

}

使用语法

@注解类型名

应用场景

  • 注解可以出现在类上、属性上、方法上、变量上等……
  • 注解还可以出现在注解类型上

注解当中的属性支持哪些类型

byte, short, int, long, float, double, boolean, char, String, Class, 枚举类型
以及以上每一种的数组形式

jdk内置的注解

@Deprecated(表示已过时)

@Override(表示重写方法)

源码

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

Override注解只能注解方法,是给编译器参考的,和运行阶段没有关系。

凡是java中的方法带有Override注解的,编译器都会进行编译检查,如果这个方法不是重写父类的方法,编译器报错。

元注解

用来标注“注解类型”的注解,称为元注解。类似上面的Override注解源码,Target注解就是元注解。

常见的元注解

  • Target

    用来标注“被标注的注解”可以出现在哪些位置上

    • @Target(ElementType.METHOD):表示”被标注的注解“只能出现在方法上
    • @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
      • 表示”被标注的注解“可以出现在构造方法上、字段上、局部变量上、方法上、包上、参数上、类上
  • Retention

    用来标注“被标注的注解”最终保存在哪里

    @Retention(RetentionPolicy.SOURCE):表示该注解只被保留在java源文件中。

    @Retention(RetentionPolicy.CLASS):表示该注解被保存在class文件中。

    @Retention(RetentionPolicy.RUNTIME):表示该注解被保存在class文件中,并且可以被反射机制所读取。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值