java快速入门

1.注释

注释的重要性不言而喻,我们不管写什么代码注释必不可少,那么java的注释的书写方式与注释模板是怎么进行的呢?我们来看一下。

package frist;
/*
 * @Description HelloWorld类
 * @Author 王延领
 **/
class HelloWorld {
    /*
    这是我们Java程序的主入口,
    main方法也是程序的主线程。
    */
    public static void main(String[] arg)
    {
        //输出
       System.out.println("wyl");
    }
}

1.1 注释

以上可以看出java的注释主要有三种
单行注释:只能注释当前行,以//开始,直到行结束

 //输出

多行注释:注释一段文字,以/*开始, */结束!

 /*
    这是我们Java程序的主入口,
    main方法也是程序的主线程。
 */

文档注释:用于生产API文档,配合JavaDoc。

/*
 * @Description HelloWorld类
 * @Author 王延领
 **/

1.2 idea注释模版配置

1.2.1 定义java文件头部的注释

File => setting => editor => File and Code Templates-class -Includes
image-20210712143531809

/**
  * @创建人 王延领
  *@创建时间 ${DATE}
  *描述 Todo
**/

以上当你创建一个class的时候就会带上以上信息了

1.2.2 给java类中的方法添加上注释

第一步勾选Enable Live Templates
首先要在上一步中勾选中 Enable Live Templates
image-20210712144107882
第二步新建一个Group
其次要打开LiveTemplates 然后新建一个Group
如图:
img
在弹窗口中输入你想要的group名称,wyl
image-20210712144433114
其中:Abbreviation 为快捷键,当输入w的时候就会提示对应的方法注释模板,j为类的注释模板
image-20210712144649673
Templete Text
注释内容,$$ 为动态模板参数点击Edit Vaariables 选择对应动态值。

image-20210712144955611

/*
 * @描述: TODO
 * @作者 王延领
 * @时间 2021/7/12
 * @版本 1.0
 */
public class wyl {
    /**
     *@描述
     *@参数 [str]
     *@返回值 [java.lang.String]
     *@创建人 王延领
     *@创建时间 2021/7/12
     *@修改人和其它信息
     */
    public String CommentTemplate(String str)
    {
        return str;
    }
}

2.关键字

关键字 说明
private 一种访问控制方式:私用模式
protected 一种访问控制方式:保护模式
public 一种访问控制方式:共用模式
abstract 表明类或者成员方法具有抽象属性
class
extends 表明一个类型是另一个类型的子类型,这里常见的类型有类和接口
final 用来说明最终属性,表明一个类不能派生出子类,或者成员方法不能被覆盖,或者成员域的值不能被改变
implements 表明一个类实现了给定的接口
interface 接口
native 用来声明一个方法是由与计算机相关的语言(如C/C++/FORTRAN语言)实现的
new 用来创建新实例对象
static 表明具有静态属性
strictfp 用来声明FP_strict(单精度或双精度浮点数)表达式遵循IEEE 754算术规范
synchronized 表明一段代码需要同步执行
transient 声明不用序列化的成员域
volatile 表明两个或者多个变量必须同步地发生变化
break 提前跳出一个块
continue 回到一个块的开始处
return 从成员方法中返回数据
do 用在do-while循环结构中
while 用在循环结构中
if 条件语句的引导词
else 用在条件语句中,表明当条件不成立时的分支
for 一种循环结构的引导词
instanceof 用来测试一个对象是否是指定类型的实例对象
switch 分支语句结构的引导词
case 用在switch语句之中,表示其中的一个分支
default 默认,例如,用在switch语句中,表明一个默认的分支
try 尝试一个可能抛出异常的程序块
catch 用在异常处理中,用来捕捉异常
throw 抛出一个异常
throws 声明在当前定义的成员方法中所有需要抛出的异常
import 表明要访问指定的类或包
package
boolean 基本数据类型之一,布尔类型
byte 基本数据类型之一,字节类型
char 基本数据类型之一,字符类型
double 基本数据类型之一,双精度浮点数类型
float 基本数据类型之一,单精度浮点数类型
int 基本数据类型之一,整数类型
long 基本数据类型之一,长整数类型
short 基本数据类型之一,短整数类型
super 表明当前对象的父类型的引用或者父类型的构造方法
this 指向当前实例对象的引用
void 声明当前成员方法没有返回值
goto 保留关键字,没有具体含义
const 保留关键字,没有具体含义

3.数据类型

image-20210712151302116

3.1.数据类型转换

3.1.1自动类型转换

自动类型转换:容量小的数据类型可以自动转换为容量大的数据类型。
image-20210712152354656
注:如果低级类型为char型,向高级类型(整型)转换时,会转换为对应ASCII码值

3.1.2 强制类型转换

强制类型转换,又被称为造型,用于显式的转换一个数值的类型.
转换方式为:(type)var ,运算符“()”中的type表示将值var想要转换成的目标数据类型。 条件是转换的数据类型必须是兼容的。

double x = 3.14;
int nx = (int)x; //值为3
char c = 'a';
int d = c+1;
System.out.println(d); //98
System.out.println((char)d); //b

3.1.3.包装类过渡类型转换

eg1:int i=Integer.parseInt(“123”)
说明:此方法只能适用于字符串转化成整型变量
eg2: float f=Float.valueOf(“123”).floatValue()
说明:上例是将一个字符串转化成一个Float对象,然后再调用这个对象的floatValue()方法返回其对应的float数值。
eg3: boolean b=Boolean.valueOf(“123”).booleanValue()
说明:上例是将一个字符串转化成一个Boolean对象,然后再调用这个对象的booleanValue()方法返回其对应的boolean数值。
eg4:double d=Double.valueOf(“123”).doublue()
说明:上例是将一个字符串转化成一个Double对象,然后再调用这个对象的doublue()方法返回其对应的double数值。
eg5: long l=Long.valueOf(“123”).longValue()
说明:上例是将一个字符串转化成一个Long对象,然后再调用这个对象的longValue()方法返回其对应的long数值。
eg6: char=Character.valueOf(“123”).charValue()
说明:上例是将一个字符串转化成一个Character对象

4.常量、变量、运算符

常量

变量是什么:就是可以变化的量!
我们通过变量来操纵存储空间中的数据,变量就是指代这个存储空间!空间位置是确定的,但是里面放
置什么值不确定!Java是一种强类型语言,每个变量都必须声明其类型。

//数据类型 变量名 = 值;可以使用逗号隔开来声明多个同类型变量。

注意事项:
每个变量都有类型,类型可以是基本类型,也可以是引用类型。
变量名必须是合法的标识符。
变量声明是一条完整的语句,因此每一个声明都必须以分号结束
变量作用域
类变量(静态变量: static variable):独立于方法之外的变量,用 static 修饰。
实例变量(成员变量:member variable):独立于方法之外的变量,不过没有 static 修饰。
局部变量(lacal variable):类的方法中的变量。

变量

常量(Constant):初始化(initialize)后不能再改变值!不会变动的值。

final 常量名=值;
final double PI=3.14;

命名规范

  1. 所有变量、方法、类名:见名知意
  2. 类成员变量:首字母小写和驼峰原则 : monthSalary
  3. 局部变量:首字母小写和驼峰原则
  4. 常量:大写字母和下划线:MAX_VALUE
  5. 类名:首字母大写和驼峰原则: Man, GoodMan
  6. 方法名:首字母小写和驼峰原则: run(), runRun()

运算符

Java 语言支持如下运算符:
算术运算符: +,-,,/,%,++,--
赋值运算符 =
关系运算符: >,<,>=,<=,==,!= instanceof
逻辑运算符: &&,||,!
位运算符: &,|,^,~ , >>,<<,>>> (了解!!!)
条件运算符 ?:
扩展赋值运算符:+=,-=,
=,/=

5.java流转控制

if...else、while、do...while、for、switch...case 在这就不累述了。
跳转:
return
return从一个方法返回,并把控制权交给调用它的语句序;或者直接结束当前的程序;
break
break语句在for、while、do···while循环语句中,经常用于强行退出当前循环;
continue
continue语句用于跳过此次循环,执行下次循环;

6.方法

那么什么是方法呢?
Java方法是语句的集合,它们在一起执行一个功能。
方法是解决一类问题的步骤的有序组合
方法包含于类或对象中
方法在程序中被创建,在其他地方被引用
设计方法的原则:方法的本意是功能块,就是实现某个功能的语句块的集合。我们设计方法的时候,最
好保持方法的原子性,就是一个方法只完成1个功能,这样利于我们后期的扩展。
方法的优点
使程序变得更简短而清晰。
有利于程序维护。
可以提高程序开发的效率。
提高了代码的重用性。

定义

修饰符 返回值类型 方法名(参数类型 参数名){
...
方法体
...
return 返回值;
}

修饰符:修饰符,这是可选的,告诉编译器如何调用该方法。定义了该方法的访问类型。
返回值类型 :方法可能会返回值。returnValueType 是方法返回值的数据类型。有些方法执行所需
的操作,但没有返回值。在这种情况下,returnValueType 是关键字void。
方法名:是方法的实际名称。方法名和参数表共同构成方法签名。
参数类型:参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参
数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。
形式参数:在方法被调用时用于接收外界输入的数据。
实参:调用方法时实际传给方法的数据。
方法体:方法体包含具体的语句,定义该方法的功能。

方法的重载

就是说一个类的两个方法拥有相同的名字,但是有不同的参数列表。

可变参数

在方法声明中,在指定参数类型后加一个省略号(...) 。
一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声
明。

typeName... parameterName

递归

自己调用自己

7.数组

数组的定义:

数组是相同类型数据的有序集合.
数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成。
其中,每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们.

数组的四个基本特点:

  1. 其长度是确定的。数组一旦被创建,它的大小就是不可以改变的。
  2. 其元素必须是相同类型,不允许出现混合类型。
  3. 数组中的元素可以是任何数据类型,包括基本类型和引用类型。
  4. 数组变量属引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量

数组声明

dataType[] arrayRefVar; // 首选的方法
或
dataType arrayRefVar[]; // 效果相同,但不是首选方法

创建数组

arrayRefVar = new dataType[1 arraySize];

数组的元素是通过索引访问的。数组索引从 0 开始,所以索引值从 0 到 arrayRefVar.length-1。

三种初始化

静态初始化
除了用new关键字来产生数组以外,还可以直接在定义数组的同时就为数组元素分配空间并赋值。

int[] a = {1,2,3};
Man[] mans = {new Man(1,1),new Man(2,2)};

动态初始化
数组定义、为数组元素分配空间、赋值的操作、分开进行。

int[] a = new int[2];
a[0]=1;
a[1]=2;

数组的默认初始化
数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实
例变量同样的方式被隐式初始化。

public static void main(String[] args) {
int[] a=new int[2];
boolean[] b = new boolean[2];
String[] s = new String[2];
System.out.println(a[0]+":"+a[1]); //0,0
System.out.println(b[0]+":"+b[1]); //false,false
System.out.println(s[0]+":"+s[1]); //null, null
}

数组边界

下标的合法区间:[0, length-1],如果越界就会报错;

for 和For-Each 循环

for(type element: array){
System.out.println(element);
}
for (int i = 1; i < myList.length; i++) {
System.out.println(myList[i]);
}

多维数组

type[][] typeName = new type[typeLength1][1 typeLength2];

Arrays 类

数组的工具类java.util.Arrays
java.util.Arrays 类能方便地操作数组. 使用之前需要导包!
具有以下常用功能:
给数组赋值:通过 fill 方法。
对数组排序:通过 sort 方法,按升序。
比较数组:通过 equals 方法比较数组中元素值是否相等。
查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作。
转换为list: 通过asList(a)进行转换

8.面向对象

万物皆为对象!!!对象是抽象概念的具体实例。

以类的方式组织代码,以对象的组织(封装)数据就是面向对象

继承

继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。

class 父类 {
}
class 子类 extends 父类 {
}

public interface A {
    public void eat();
    public void sleep();
}
 
public interface B {
    public void show();
}
 
public class C implements A,B {
}

为什么要继承,因为有重复。所以才继承,进而我们就知道了。父类就是公共部分的定义或规则

Java 不支持多继承(只能继承一个类),但支持多重继承。

image-20210809181238809

特点

  • 子类拥有父类非 private 的属性、方法。

  • 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。

  • 子类可以用自己的方式实现父类的方法。

  • Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 B 类继承 A 类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。

  • 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)

super 与 this 关键字

class Animal {
  void eat() {
    System.out.println("animal : eat");
  }
}
 
class Dog extends Animal {
  void eat() {
    System.out.println("dog : eat");
  }
  void eatTest() {
    this.eat();   // this 调用自己的方法
    super.eat();  // super 调用父类方法
  }
}
 
public class Test {
  public static void main(String[] args) {
    Animal a = new Animal();
    a.eat();
    Dog d = new Dog();
    d.eatTest();
  }
}
animal : eat
dog : eat
animal : eat

final关键字
final 关键字声明类可以把类定义为不能继承的,即最终类;

class SuperClass {
  private int n;
  SuperClass(){
    System.out.println("SuperClass()");
  }
  SuperClass(int n) {
    System.out.println("SuperClass(int n)");
    this.n = n;
  }
}
// SubClass 类继承
class SubClass extends SuperClass{
  private int n;
  
  SubClass(){ // 自动调用父类的无参数构造器
    System.out.println("SubClass");
  }  
  
  public SubClass(int n){ 
    super(300);  // 调用父类中带有参数的构造器
    System.out.println("SubClass(int n):"+n);
    this.n = n;
  }
}
// SubClass2 类继承
class SubClass2 extends SuperClass{
  private int n;
  
  SubClass2(){
    super(300);  // 调用父类中带有参数的构造器
    System.out.println("SubClass2");
  }  
  
  public SubClass2(int n){ // 自动调用父类的无参数构造器
    System.out.println("SubClass2(int n):"+n);
    this.n = n;
  }
}
public class TestSuperSub{
  public static void main (String args[]){
    System.out.println("------SubClass 类继承------");
    SubClass sc1 = new SubClass();
    SubClass sc2 = new SubClass(100); 
    System.out.println("------SubClass2 类继承------");
    SubClass2 sc3 = new SubClass2();
    SubClass2 sc4 = new SubClass2(200); 
  }
}
------SubClass 类继承------
SuperClass()
SubClass
SuperClass(int n)
SubClass(int n):100
------SubClass2 类继承------
SuperClass(int n)
SubClass2
SuperClass()
SubClass2(int n):200

构造函数

子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。

如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。

重写(Override)与重载(Overload)

重写(Override)

重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写

class Animal{
   public void move(){
      System.out.println("动物可以移动");
   }
}
 
class Dog extends Animal{
   public void move(){
      System.out.println("狗可以跑和走");
   }
}
 
public class TestDog{
   public static void main(String args[]){
      Animal a = new Animal(); // Animal 对象
      Animal b = new Dog(); // Dog 对象
      a.move();// 执行 Animal 类的方法
      b.move();//执行 Dog 类的方法
   }
}
动物可以移动
狗可以跑和走

方法的重写规则

  • 参数列表与被重写方法的参数列表必须完全相同。

  • 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。

  • 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。

  • 父类的成员方法只能被它的子类重写。

  • 声明为 final 的方法不能被重写。

  • 声明为 static 的方法不能被重写,但是能够被再次声明。

  • 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。

  • 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。

  • 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。

  • 构造方法不能被重写。

  • 如果不能继承一个类,则不能重写该类的方法。

重载(Overload)

重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。

每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。

最常用的地方就是构造器的重载。

public class Overloading {
    public int test(){
        System.out.println("test1");
        return 1;
    }
 
    public void test(int a){
        System.out.println("test2");
    }   
 
    //以下两个参数类型顺序不同
    public String test(int a,String s){
        System.out.println("test3");
        return "returntest3";
    }   
 
    public String test(String s,int a){
        System.out.println("test4");
        return "returntest4";
    }   
 
    public static void main(String[] args){
        Overloading o = new Overloading();
        System.out.println(o.test());
        o.test(1);
        System.out.println(o.test(1,"test3"));
        System.out.println(o.test("test4",1));
    }
}

重载规则:

  • 被重载的方法必须改变参数列表(参数个数或类型不一样);

  • 被重载的方法可以改变返回类型;

  • 被重载的方法可以改变访问修饰符;

  • 被重载的方法可以声明新的或更广的检查异常;

  • 方法能够在同一个类中或者在一个子类中被重载。

  • 无法以返回值类型作为重载函数的区分标准。

    区别点 重载方法 重写方法
    参数列表 必须修改 一定不能修改
    返回类型 可以修改 一定不能修改
    异常 可以修改 可以减少或删除,一定不能抛出新的或者更广的异常
    访问 可以修改 一定不能做更严格的限制(可以降低限制)

多态

多态是同一个行为具有多个不同表现形式或形态的能力。

多态就是同一个接口,使用不同的实例而执行不同操作。

多态性是对象多种表现形式的体现。

多态的优点

  1. 消除类型之间的耦合关系
  2. 可替换性
  3. 可扩充性
  4. 接口性
  5. 灵活性
  6. 简化性

多态存在的三个必要条件

继承
重写
父类引用指向子类对象:Parent p = new Child();

class Shape {
    void draw() {}
}
 
class Circle extends Shape {
    void draw() {
        System.out.println("Circle.draw()");
    }
}
 
class Square extends Shape {
    void draw() {
        System.out.println("Square.draw()");
    }
}
 
class Triangle extends Shape {
    void draw() {
        System.out.println("Triangle.draw()");
    }
}

虚函数

虚函数的存在是为了多态。

Java 中其实没有虚函数的概念,它的普通函数就相当于 C++ 的虚函数,动态绑定是Java的默认行为。如果 Java 中不希望某个函数具有虚函数特性,可以加上 final 关键字变成非虚函数。

多态的实现方式

方式一:重写:

方式二:接口

方式三:抽象类和抽象方法

抽象类

拥有抽象方法的类就是抽象类,抽象类要使用abstract关键字声明.

abstract class A{//定义一个抽象类
	public void fun(){//普通方法
		System.out.println("存在方法体的方法");
	}
	public abstract void print();//抽象方法,没有方法体,有abstract关键字做修饰
	
}

继承抽象类

我们可以通过以下方式继承 Employee 类的属性

抽象类的使用原则
(1)抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public;
(2)抽象类不能直接实例化,需要依靠子类采用向上转型的方式处理;
(3)抽象类必须有子类,使用extends继承,一个子类只能继承一个抽象类;
(4)子类(如果不是抽象类)则必须覆写抽象类之中的全部抽象方法(如果子类没有实现父类的抽象方法,则必须将子类也定义为为abstract类。);

package com.wz.abstractdemo;

abstract class A{//定义一个抽象类
	
	public void fun(){//普通方法
		System.out.println("存在方法体的方法");
	}
	
	public abstract void print();//抽象方法,没有方法体,有abstract关键字做修饰
	
}
//单继承
class B extends A{//B类是抽象类的子类,是一个普通类

	@Override
	public void print() {//强制要求覆写
		System.out.println("Hello World !");
	}
	
}
public class TestDemo {

	public static void main(String[] args) {
		A a = new B();//向上转型
		
		a.print();//被子类所覆写的过的方法
	}
}
Hello World !

封装

封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。

封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。

要访问该类的代码和数据,必须通过严格的接口控制。

封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。

适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。

封装的优点

  1. 良好的封装能够减少耦合。
  2. 类内部的结构可以自由修改。
  3. 可以对成员变量进行更精确的控制。
  4. 隐藏信息,实现细节。

接口

在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。

接口与类相似点

  1. 一个接口可以有多个方法。
  2. 接口文件保存在 .java 结尾的文件中,文件名使用接口名。
  3. 接口的字节码文件保存在 .class 结尾的文件中。
  4. 接口相应的字节码文件必须在与包名称相匹配的目录结构中。

接口与类的区别

  1. 接口不能用于实例化对象。
  2. 接口没有构造方法。
  3. 接口中所有的方法必须是抽象方法,Java 8 之后 接口中可以使用 default 关键字修饰的非抽象方法。
  4. 接口不能包含成员变量,除了 static 和 final 变量。
  5. 接口不是被类继承了,而是要被类实现。
  6. 接口支持多继承。

接口特性

  1. 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
  2. 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
  3. 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。

抽象类和接口的区别

  1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。

  2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。

  3. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法

  4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。

[可见度] interface 接口名称 [extends 其他的接口名] {
        // 声明变量
        // 抽象方法
}
/* 文件名 : NameOfInterface.java */
import java.lang.*;
//引入包
 
public interface NameOfInterface
{
   //任何类型 final, static 字段
   //抽象方法
}

接口有以下特性

  • 接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
  • 接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。
  • 接口中的方法都是公有的。

枚举

枚举是一个特殊的类,一般表示一组常量.每个枚举都是通过 Class 在内部实现的,且所有的枚举值都是 public static final 的。

enum Color
{
    RED, GREEN, BLUE;
}
 
public class Test
{
    // 执行输出结果
    public static void main(String[] args)
    {
        Color c1 = Color.RED;
        System.out.println(c1);
    }
}
RED

values(), ordinal() 和 valueOf() 方法

enum 定义的枚举类默认继承了 java.lang.Enum 类,并实现了 java.lang.Seriablizable 和 java.lang.Comparable 两个接口。

values(), ordinal() 和 valueOf() 方法位于 java.lang.Enum 类中:

  • values() 返回枚举类中所有的值。

  • ordinal()方法可以找到每个枚举常量的索引,就像数组索引一样。

  • valueOf()方法返回指定字符串值的枚举常量。

    enum Color
    {
        RED, GREEN, BLUE;
    }
     
    public class Test
    {
        public static void main(String[] args)
        {
            // 调用 values()
            Color[] arr = Color.values();
            // 迭代枚举
            for (Color col : arr)
            {
                // 查看索引
                System.out.println(col + " at index " + col.ordinal());
            }
            // 使用 valueOf() 返回枚举常量,不存在的会报错 IllegalArgumentException
            System.out.println(Color.valueOf("RED"));
            // System.out.println(Color.valueOf("WHITE"));
        }
    }
    
    RED at index 0
    GREEN at index 1
    BLUE at index 2
    RED
    

    枚举成员

    枚举跟普通类一样可以用自己的变量、方法和构造函数,构造函数只能使用 private 访问修饰符,所以外部无法调用。

    enum Color
    {
        RED, GREEN, BLUE;
     
        // 构造函数
        private Color()
        {
            System.out.println("Constructor called for : " + this.toString());
        }
        public void colorInfo()
        {
            System.out.println("Universal Color");
        }
    }
    

包(package)

为了更好地组织类,Java 提供了包机制,用于区别类名的命名空间。

包的 3 个作用如下

  1. 区分相同名称的类。

  2. 能够较好地管理大量的类。

  3. 控制访问范围。

定义

 ```java

package 包名;
```
Java 包的命名规则如下:

  • 包名全部由小写字母(多个单词也全部小写)。
  • 如果包名包含多个层次,每个层次用“.”分割。
  • 包名一般由倒置的域名开头,比如 com.baidu,不要有 www。
  • 自定义包不能 java 开头

包导入

\\如果使用不同包中的其它类,需要使用该类的全名(包名+类名)
    example.Test test = new example.Test();
\\import 包名+类名;
import example.Test;\\or
import example.*;

系统包

说明
java.lang Java 的核心类库,包含运行 Java 程序必不可少的系统类,如基本数据类型、基本数学函数、 字符串处理、异常处理和线程类等,系统默认加载这个包
java.io Java 语言的标准输入/输出类库,如基本输入/输出流、文件输入/输出、过滤输入/输出流等
java.util 包含如处理时间的 Date 类,处理动态数组的 Vector 类,以及 Stack 和 HashTable 类
java.awt 构建图形用户界面(GUI)的类库,低级绘图操作 Graphics 类、图形界面组件和布局管理 (如 Checkbox 类、Container 类、LayoutManger 接口等),以及用户界面交互控制和事 件响应(如 Event 类)
java.awt.image 处理和操纵来自网上的图片的 Java 工具类库
java.wat.peer 很少在程序中直接用到,使得同一个 Java 程序在不同的软硬件平台上运行
java.net 实现网络功能的类库有 Socket 类、ServerSocket 类
java.lang.reflect 提供用于反射对象的工具
java.util.zip 实现文件压缩功能
java.awt.datatransfer 处理数据传输的工具类,包括剪贴板、字符串发送器等
java.sql 实现 JDBC 的类库
java.rmi 提供远程连接与载入的支持
java. security 提供安全性方面的有关支持

9.异常处理

异常处理的概念

是编程语言或计算机硬件里的一种机制,用于处理软件或信息系统中出现的异常状况(即超出程序正常执行流程的某些特殊条件)。

关键字

Java异常机制用到的几个关键字:try、catch、finally、throw、throws。

try -- 用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常
时,异常就被抛出。
catch -- 用于捕获异常。catch用来捕获try语句块中发生的异常。
finally -- finally语句块总是会被执行。它主要用于回收在try块里打开的物力资源(如数据库连接、网络
连接和磁盘文件)。只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw语
句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。
throw -- 用于抛出异常。
throws -- 用在方法签名中,用于声明该方法可能抛出的异常。

 try{
        可能会发生的异常
    }catch(异常类型 异常名(变量)){
        针对异常进行处理的代码
    }catch(异常类型 异常名(变量)){
        针对异常进行处理的代码
    }...
    [finally{
        释放资源代码;
    }]

Error与Exception区别

Error(错误)是系统中的错误,程序员是不能改变的和处理的,是在程序编译时出现的错误,只能通过修改程序才能修正。一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢等。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止。
Exception(异常)表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。

throw与throws区别

throw:指的是在方法中人为抛出一个异常对象(这个异常对象可能是自己实例化或者抛出已存在的);
throw ThrowableInstance;
throws:在方法的声明上使用,表示此方法在调用时必须处理异常。
throw new NullPointerException("demo");
image-20210728140018762

​ Java异常层次结构图(网上获取)

10.集合框架

所有集合类都位于 java.util 包下。Java的集合类主要由两个接口派生而出:Collection 和 Map,Collection 和 Map 是 Java 集合框架的根接口,这两个接口又包含了一些子接口或实现类。

集合框架被设计成要满足以下几个目标:

  • 该框架必须是高性能的。基本集合(动态数组,链表,树,哈希表)的实现也必须是高效的。
  • 该框架允许不同类型的集合,以类似的方式工作,具有高度的互操作性。
  • 对一个集合的扩展和适应必须是简单的。

集合框架都包含如下内容:

  • 接口:是代表集合的抽象数据类型。例如 Collection、List、Set、Map 等。之所以定义多个接口,是为了以不同的方式操作集合对象

  • 实现(类):是集合接口的具体实现。从本质上讲,它们是可重复使用的数据结构,例如:ArrayList、LinkedList、HashSet、HashMap。

  • 算法:是实现集合接口的对象里的方法执行的一些有用的计算,例如:搜索和排序。这些算法被称为多态,那是因为相同的方法可以在相似的接口上有着不同的实现。

    image-20210728172402028

Collection是一个基本的集合接口,Collection中可以容纳一组集合元素(Element)

Collection 接口

Collection 是最基本的集合接口,一个 Collection 代表一组 Object,即 Collection 的元素, Java不提供直接继承自Collection的类,只提供继承于的子接口(如List和set)。

List

List接口是一个有序, 元素可重复的 Collection,使用此接口能够精确的控制每个元素插入的位置,能够通过索引(元素在List中位置,类似于数组的下标)来访问List中的元素,第一个元素的索引为 0,而且允许有相同的元素。

  1. ArrayList

底层数据结构是数组,查改快,增删慢。

非线程安全,效率高

方法:

image-20210728173620540

排序

import java.util.Collections;  // 引入 Collections 类
Collections.sort(sites); *// 字母排序*
  1. Vector

底层数据结构是数组,查改快,增删慢。

线程安全,效率低

  1. LinkedList

底层数据结构是链表,查改慢,增删快。

非线程安全,效率高

以下情况使用 LinkedList :

  • 你需要通过循环迭代来访问列表中的某些元素。
  • 需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作。

LinkedList 继承了 AbstractSequentialList 类。

LinkedList 实现了 Queue 接口,可作为队列使用。

LinkedList 实现了 List 接口,可进行列表的相关操作。

LinkedList 实现了 Deque 接口,可作为队列使用。

LinkedList 实现了 Cloneable 接口,可实现克隆。

LinkedList 实现了 java.io.Serializable 接口,即可支持序列化,能通过序列化去传输。

方法:

image-20210728174512617

Set

Set 接口存储一组唯一,无序的对象。

  1. HashSet

底层数据结构是哈希表。(无序,唯一)

依赖两个方法:hashCode()和equals() 保证元素唯一性

// 引入 HashSet 类      
import java.util.HashSet;

public class RunoobTest {
    public static void main(String[] args) {
    HashSet<String> sites = new HashSet<String>();
        sites.add("Google");
        sites.add("Runoob");
        sites.add("Taobao");
        sites.add("Zhihu");
        sites.add("Runoob");  // 重复的元素不会被添加
        System.out.println(sites);
    }
}

以上代码只会输出一个Runoob。

  1. LinkedHashSet

底层数据结构是链表和哈希表。(FIFO插入有序,唯一)

1.由链表保证元素有序

2.由哈希表保证元素唯一

  1. TreeSet

底层数据结构是红黑树。(唯一,有序)

如何保证元素排序的呢? 自然排序,比较器排序

Set和List的区别

  • Set 接口实例存储的是无序的,不重复的数据。List 接口实例存储的是有序的,可以重复的元素。
  • Set检索效率低下,删除和插入效率高,插入和删除不会引起元素位置改变 <实现类有HashSet,TreeSet>
  • List和数组类似,可以动态增长,根据实际存储的数据的长度自动增长List的长度。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变 <实现类有ArrayList,LinkedList,Vector>

image-20210728172343233

Map与Collection是并列关系。Map提供键(key)到值(value)的映射。一个Map中不能包含相同的键,每个键只能映射一个值。

  1. HashMap

无序,非线程安全,效率高。HashMap允许null值(key和value都允许)。

image-20210728174945196

  1. HashTable

无序,线程安全,效率低。除构造函数外,HashTable的所有 public 方法声明中都有 synchronized关键字,而HashMap的源码中则没有。HashTable不允许null值(key和value都允许)。

  1. TreeMap

有序,非线程安全,效率高(O(logN)),但比不上HashMap (O(1))。

11.流(Stream)、文件(File)和IO

Java.io 包中定义了多个流类型(类或抽象类)来实现输入/输出功能;

img

可以从不同的角度对其进行分
类:
1.按数据流的方向不同可以分为输入流【InputStream(字节流),Reader(字符流)】和输出流【OutPutStream(字节流),Writer(字符流)】
2.按照处理数据单位不同可以分为字节流【一个字节(Byte)是8位(bit))】和字符流【一个字符是2个字节】
3.按照功能不同可以分为节点流和处理流

image-20210905011146261

4.按照操作对象分

img

InputStream 和 OutputStream

img

img

import java.io.*;
 
public class fileStreamTest {
    public static void main(String[] args) {
        try {
            byte bWrite[] = { 11, 21, 3, 40, 5 };
            OutputStream os = new FileOutputStream("test.txt");
            for (int x = 0; x < bWrite.length; x++) {
                os.write(bWrite[x]); // writes the bytes
            }
            os.close();
 
            InputStream is = new FileInputStream("test.txt");
            int size = is.available();
 
            for (int i = 0; i < size; i++) {
                System.out.print((char) is.read() + "  ");
            }
            is.close();
        } catch (IOException e) {
            System.out.print("Exception");
        }
    }
}

上面的程序首先创建文件test.txt,并把给定的数字以二进制形式写进该文件,同时输出到控制台上。

以上代码由于是二进制写入,可能存在乱码,你可以使用以下代码实例来解决乱码问题:

//文件名 :fileStreamTest2.java
import java.io.*;
 
public class fileStreamTest2 {
    public static void main(String[] args) throws IOException {
        File f = new File("a.txt");
        FileOutputStream fop = new FileOutputStream(f);
        // 构建FileOutputStream对象,文件不存在会自动新建
        OutputStreamWriter writer = new OutputStreamWriter(fop, "UTF-8");
        // 构建OutputStreamWriter对象,参数可以指定编码,默认为操作系统默认编码,windows上是gbk
        writer.append("中文输入");
        // 写入到缓冲区
        writer.append("\r\n");
        // 换行
        writer.append("English");
        // 刷新缓存冲,写入到文件,如果下面已经没有写入的内容了,直接close也会写入
        writer.close();
        // 关闭写入流,同时会把缓冲区内容写入文件,所以上面的注释掉
        fop.close();
        // 关闭输出流,释放系统资源
        FileInputStream fip = new FileInputStream(f);
        // 构建FileInputStream对象
        InputStreamReader reader = new InputStreamReader(fip, "UTF-8");
        // 构建InputStreamReader对象,编码与写入相同
        StringBuffer sb = new StringBuffer();
        while (reader.ready()) {
            sb.append((char) reader.read());
            // 转成char加到StringBuffer对象中
        }
        System.out.println(sb.toString());
        reader.close();
        // 关闭读取流
        fip.close();
        // 关闭输入流,释放系统资源
 
    }
}

Reader 流与Writer流

img

img

Reader ,Write与InputStream ,OutputStream: 唯一的区别就在于读的数据单位不同分别为(16bit),(8bit)

创建读取目录:

import java.io.File;
 
public class CreateDir {
    public static void main(String[] args) {
        String dirname = "/tmp/user/java/bin";
        File d = new File(dirname);
        // 现在创建目录
        d.mkdirs();
    }
}
import java.io.File;
 
public class DirList {
    public static void main(String args[]) {
        String dirname = "/tmp";
        File f1 = new File(dirname);
        if (f1.isDirectory()) {
            System.out.println("目录 " + dirname);
            String s[] = f1.list();
            for (int i = 0; i < s.length; i++) {
                File f = new File(dirname + "/" + s[i]);
                if (f.isDirectory()) {
                    System.out.println(s[i] + " 是一个目录");
                } else {
                    System.out.println(s[i] + " 是一个文件");
                }
            }
        } else {
            System.out.println(dirname + " 不是一个目录");
        }
    }
}

删除

import java.io.File;
 
public class DeleteFileDemo {
    public static void main(String[] args) {
        // 这里修改为自己的测试目录
        File folder = new File("/tmp/java/");
        deleteFolder(folder);
    }
 
    // 删除文件及目录
    public static void deleteFolder(File folder) {
        File[] files = folder.listFiles();
        if (files != null) {
            for (File f : files) {
                if (f.isDirectory()) {
                    deleteFolder(f);
                } else {
                    f.delete();
                }
            }
        }
        folder.delete();
    }
}

缓存流

是处理流的一种,它是要“套接”在相应的节点流之上,对读写的数据提供了缓冲的功能,避免频繁读写硬盘, 提高了读写的效率。同时增加了一些新的方法。

BufferedReader(Reader in)
BufferedReader(Reader in,int sz) //sz 为自定义缓冲区的大小
BufferedWriter(Writer out)
BufferedWriter(Writer out,int sz)
BufferedInputStream(InputStream in)
BufferedInputStream(InputStream in,int size)
BufferedOutputStream(InputStream in)
BufferedOutputStream(InputStream in,int size)

BufferedInputStream

package com.kuang.chapter;
import java.io.*;
public class TestBufferStream {
public static void main(String args[]) {
FileInputStream fis = null;
File f = new File("a.txt");
try {
fis = new FileInputStream( f);
// 在FileInputStream节点流的外面套接一层处理流BufferedInputStream
BufferedInputStream bis = new BufferedInputStream(fis);
int c = 0;
System.out.println((char) bis.read());
System.out.println((char) bis.read());
bis.mark(100);// 在第100个字符处做一个标记
for (int i = 0; i <= 10 && (c = bis.read()) != -1; i++) {
System.out.print((char) c);
}
System.out.println();
bis.reset();// 重新回到原来标记的地方
for (int i = 0; i <= 10 && (c = bis.read()) != -1; i++) {
System.out.print((char) c);
}
bis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (Exception e1) {
e1.printStackTrace();
}
}
}

BufferedReader

package com.kuang.chapter;
import java.io.*;
public class TestBufferStream{
public static void main(String args[]){
try{
BufferedWriter bw = new BufferedWriter(new FileWriter("a\\Student.txt"));
//在节点流FileWriter的外面再套一层处理流BufferedWriter
String s = null;
for(int i=0;i<100;i++){
s = String.valueOf(Math.random());//“Math.random()”将会生成一系列介于0~1之间的随机数。
// static String valueOf(double d)这个valueOf()方法的作用就是把
一个double类型的数转换成字符串
//valueOf()是一个静态方法,所以可以使用“类型.静态方法名”的形式来调用
bw.write(s);//把随机数字符串写入到指定文件中
bw.newLine();//调用newLine()方法使得每写入一个随机数就换行显示
}
bw.flush();//调用flush()方法清空缓冲区
BufferedReader br = new BufferedReader(new FileReader("a:\\Student.txt"));
//在节点流FileReader的外面再套一层处理流BufferedReader
while((s = br.readLine())!=null){
//使用BufferedReader处理流里面提供String readLine()方法读取文件中的数据时是一行一行读取的
//循环结束的条件就是使用readLine()方法读取数据返回的字符串为空值后则表
示已经读取到文件的末尾了。
System.out.println(s);
}
bw.close();
br.close();
}catch(Exception e){
e.printStackTrace();
}
}
}

转换流

InputStreamReader 和 OutputStreamWriter 用于字节数据到字符数据之间的转换
InputStreamReader 需要和 InputStream “套接” 。
OutputStreamWriter 需要和 OutputStream “套接” 。
转换流在构造时可以指定其编码集合

import java.io.*;
public class TestTransform1 {
public static void main(String args[]) {
try {
    OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:/char.txt"));
    osw.write("熊方园真烦人");// 把字符串写入到指定的文件中去
    System.out.println(osw.getEncoding());// 使用getEncoding()方法取得当前系统的默认字符编码
    osw.close();
    osw = new OutputStreamWriter(new FileOutputStream("D:\\java\\char.txt", true), "utf-8");// 如果在调用FileOutputStream的构造方法时没有加入true,那么新加入的字符    串就会替换掉原来写入的字符串,在调用构造方法时指定了字符的编码
    osw.write("不想搭理她");// 再次向指定的文件写入字符串,新写入的字符串加入到原来字符串的后面
    System.out.println(osw.getEncoding());
    osw.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

数据流

数据流 DataInputStream DataOutputStream 【分别继承自InputStream 和 OutputStream】等-提供将基础数据类型写入到文件中,或者读取出来.提供了可以存取与机器无关的Java原始类型数据(int,double等)的方法。

public static void main(String args[]){
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//在调用构造方法时,首先会在内存里面创建一个ByteArray字节数组
DataOutputStream dos = new DataOutputStream(baos);
//在输出流的外面套上一层数据流,用来处理int,double类型的数
try{
    dos.writeDouble(Math.random());//把产生的随机数直接写入到字节数组
    ByteArray中
    dos.writeBoolean(true);//布尔类型的数据在内存中就只占一个字节
    ByteArrayInputStream bais = new
    ByteArrayInputStream(baos.toByteArray());
        System.out.println(bais.available());
    DataInputStream dis = new DataInputStream(bais);
    System.out.println(dis.readDouble());//先写进去的就先读出来,调用readDouble()方法读取出写入的随机数
    System.out.println(dis.readBoolean());//后写进去的就后读出来,这里面的读取顺序不能更改位置,否则会打印出不正确的结果
    dos.close();
    bais.close();
}catch(Exception e){
e.printStackTrace();
}
}

打印流

打印流是输出信息最方便的类,注意包含字节打印流PrintStream和字符打印流:PrintWriter。打印流提供了非常方便的打印功能,
可以打印任何类型的数据信息,例如:小数,整数,字符串。


对象流

对象的输入输出流的作用: 用于写入对象 的信息和读取对象的信息。 使得对象持久化。
ObjectInputStream : 对象输入流
ObjectOutPutStream :对象输出流

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

//创建要写入磁盘的类,这个类需要实现接口 Serializable(可系列化的)
class Student implements Serializable{
    // 在这里保证了serialVersionUID 的唯一性,防止属性变量的临时改变,从而造成写入id与读取id不同
    private static final long serialVersionUID = 1L;
    int id ; //额外需要添加一个属性
    String name ;
    transient String sex; //transient修饰属性,表示暂时的,则这个属性不会被写入磁盘
    transient int age;
    public Student(String name,String sex,int age){
        this.name = name;
        this.sex = sex;
        this.age = age;
    }
}

public class objectIO {

    /**
     * @param args
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // TODO Auto-generated method stub

        createObj();
        readObj();
    }
    //(一)先写入对象
    public static void createObj() throws IOException {
        //1.创建目标路径
        File file = new File("C:\\Users\\bg\\Desktop\\objTest.txt");
        //2.创建流通道
        FileOutputStream fos = new FileOutputStream(file);
        //3.创建对象输出流
        ObjectOutputStream objOP = new ObjectOutputStream(fos);
        //4.创建类对象,并初始化
        Student stu = new Student("玛丽苏", "男", 18);
        //5.向目标路径文件写入对象
        objOP.writeObject(stu);
        //6.关闭资源
        objOP.close();
    }
    //再读取对象
    public static void readObj() throws IOException, ClassNotFoundException {
        File file = new File("C:\\Users\\bg\\Desktop\\objTest.txt");
        FileInputStream fis = new FileInputStream(file);
        ObjectInputStream objIP = new ObjectInputStream(fis);
        //读取对象数据,需要将对象流强制转换为 要写入对象的类型
        Student stu = (Student)objIP.readObject();
        System.out.println("\n name:"+stu.name+"\n sex:"+stu.sex+"\n age:"+stu.age);
        objIP.close();
    }
}

流的关闭顺序

  1. 一般情况下是:先打开的后关闭,后打开的先关闭
  2. 另一种情况:看依赖关系,如果流a依赖流b,应该先关闭流a,再关闭流b。例如,处理流a依赖节点流b,应该先关闭处理流a,再关闭节点流b
  3. 可以只关闭处理流,不用关闭节点流。处理流关闭的时候,会调用其处理的节点流的关闭方法。

12.多线程

进程与线程

线程的创建

继承Thread类,实现Runnable接口,实现Callable接口

1.继承Thread类

public class ThreadCreateDemo1 {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start(); //调用start()方法启动线程,线程不一定立即执行,CPU安排调度
    }
}
class MyThread extends Thread {//继承Thread类
    @Override
    public void run() {//重写run()方法,编写线程执行体
        super.run();
        System.out.println("hellow_world!");
    }
}

2.实现Runnable接口

public class ThreadCreateDemo2 {
    //创建线程对象,调用start()方法启动线程
    public static void main(String[] args) {
        Runnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
    }
}
class MyRunnable implements Runnable {
    public void run() {
        System.out.println("通过Runnable创建的线程!");
    }
}

上述两种创建方式,工作时性质一样。但是建议使用*实现Runable接口*方式。解决单继承的局限性。

3.实现Callable接口

public class ThreadCreateDemo3 implements Callable<Integer>{
    // 实现call方法,作为线程执行体
    public Integer call(){
        int i = 0;
        for ( ; i < 100 ; i++ ){
            System.out.println(Thread.currentThread().getName()+ "\t" + i);
        }
        // call()方法可以有返回值
        return i;
    }
    public static void main(String[] args) {
        // 创建Callable对象
        ThreadCreateDemo3 myCallableTest = new ThreadCreateDemo3();
        // 使用FutureTask来包装Callable对象
        FutureTask<Integer> task = new FutureTask<Integer>(myCallableTest);
        for (int i = 0 ; i < 100 ; i++){
            System.out.println(Thread.currentThread().getName()+ " \t" + i);
            if (i == 20){
                // 实质还是以Callable对象来创建、并启动线程
                new Thread(task , "callable").start();
            }
        }
        try{
            // 获取线程返回值
            System.out.println("callable返回值:" + task.get());
        }
        catch (Exception ex){
            ex.printStackTrace();
        }
    }
}
  1. 实现Callable接口,需要返回值类型

  2. 重写call方法,需要抛出异常

  3. 创建目标对象

  4. 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);

  5. 提交执行:Future result1 = ser.submit(t1);

  6. 获取结果:boolean r1 = result1.get()

  7. 关闭服务:ser.shutdownNow();
    总结

  8. 不过实现Runnable接口与实现Callable接口的方式基本相同,只是Callable接口里定义的方法有返回值,可以声明抛出异常而已。 因此可以将实现Runnable接口和实现Callable接口归为一种方式。

  9. Runnable、Callable接口的方式创建多线程,所以非常适合多个相同线程来处理同一份资源的情况,如果需要访问当前线程,则必须使用Thread.currentThread()方法

  10. 采用继承Thread类的方式创建多线程,因为线程类已经继承了Thread类,所以不能再继承其他父类

生命周期

线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态

下载

image-20210809153146504
Thread.State:

  1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。

  2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。
    线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。

  3. 阻塞(BLOCKED):表示线程阻塞于锁。

  4. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。

  5. 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。

  6. 终止(TERMINATED):表示该线程已经执行完毕

线程的优先级

Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度
器按照优先级决定应该调度哪个线程来执行。
线程的优先级用数字表示,范围从1~10.
hread.MIN_PRIORITY = 1;
Thread.MAX_PRIORITY = 10;
Thread.NORM_PRIORITY = 5;
使用以下方式改变或获取优先级
getPriority() . setPriority(int xxx)

线程方法

1 public void start() 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
2 public void run() 如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
3 public final void setName(String name) 改变线程名称,使之与参数 name 相同。
4 public final void setPriority(int priority) 更改线程的优先级。
5 public final void setDaemon(boolean on) 将该线程标记为守护线程或用户线程。
6 public final void join(long millisec) 等待该线程终止的时间最长为 millis 毫秒。
7 public void interrupt() 中断线程。
8 public final boolean isAlive() 测试线程是否处于活动状态。
9 public static void yield() 线程礼让: 暂停当前正在执行的线程对象,并执行其他线程。
10 public static void sleep(long millisec) 线程休眠: 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
11 public static boolean holdsLock(Object x) 当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。
12 public static Thread currentThread() 返回对当前正在执行的线程对象的引用。
13 public static void dumpStack() 将当前线程的堆栈跟踪打印至标准错误流。

停止线程:jdk提供了stop,但不建议使用可以自己去停止它

守护(daemon)线程

线程分为前台线程与后台线程(用户线程与守护线程)
虚拟机必须确保用户线程执行完毕
虚拟机不用等待守护线程执行完毕

并发,队列 和 锁,死锁

同一个对象被多个线程同时操作就是并发。

多个线程访问同一个对象, 并且某些线程还想修改这个对象 .这时候我们就需要线程同步 . 线程同步其实就是一种等待机制 , 多个需要同时访问此对象的线程进入这个对象的等待池 形成队列, 等待前面线程使用完毕 , 下一个线
程再使用。

上面的并发问题我们会加一个锁(synchronized)来解决。我锁上门的时候你们都别进来。但是加上锁之后会有以下为:

  1. 一个线程持有锁会导致其他所有需要此锁的线程挂起 ;

  2. 在多线程竞争下 , 加锁 , 释放锁会导致比较多的上下文切换 和 调度延时,引起性能问题 ;

  3. 如果一个优先级高的线程等待一个优先级低的线程释放锁 会导致优先级倒置 , 引起性能问题 .

    java 死锁产生的四个必要条件:

  • 1、互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用

  • 2、不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。

  • 3、请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。

  • 4、循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。

    死锁的情况下如果打破上述任何一个条件,便可让死锁消失。

import java.util.Date;
 
public class LockTest {
   public static String obj1 = "obj1";
   public static String obj2 = "obj2";
   public static void main(String[] args) {
      LockA la = new LockA();
      new Thread(la).start();
      LockB lb = new LockB();
      new Thread(lb).start();
   }
}
class LockA implements Runnable{
   public void run() {
      try {
         System.out.println(new Date().toString() + " LockA 开始执行");
         while(true){
            synchronized (LockTest.obj1) {
               System.out.println(new Date().toString() + " LockA 锁住 obj1");
               Thread.sleep(3000); // 此处等待是给B能锁住机会
               synchronized (LockTest.obj2) {
                  System.out.println(new Date().toString() + " LockA 锁住 obj2");
                  Thread.sleep(60 * 1000); // 为测试,占用了就不放
               }
            }
         }
      } catch (Exception e) {
         e.printStackTrace();
      }
   }
}
class LockB implements Runnable{
   public void run() {
      try {
         System.out.println(new Date().toString() + " LockB 开始执行");
         while(true){
            synchronized (LockTest.obj2) {
               System.out.println(new Date().toString() + " LockB 锁住 obj2");
               Thread.sleep(3000); // 此处等待是给A能锁住机会
               synchronized (LockTest.obj1) {
                  System.out.println(new Date().toString() + " LockB 锁住 obj1");
                  Thread.sleep(60 * 1000); // 为测试,占用了就不放
               }
            }
         }
      } catch (Exception e) {
         e.printStackTrace();
      }
   }
}

结果

Tue May 05 10:51:06 CST 2015 LockB 开始执行
Tue May 05 10:51:06 CST 2015 LockA 开始执行
Tue May 05 10:51:06 CST 2015 LockB 锁住 obj2
Tue May 05 10:51:06 CST 2015 LockA 锁住 obj1

解决

import java.util.Date;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
 
public class UnLockTest {
   public static String obj1 = "obj1";
   public static final Semaphore a1 = new Semaphore(1);
   public static String obj2 = "obj2";
   public static final Semaphore a2 = new Semaphore(1);
 
   public static void main(String[] args) {
      LockAa la = new LockAa();
      new Thread(la).start();
      LockBb lb = new LockBb();
      new Thread(lb).start();
   }
}
class LockAa implements Runnable {
   public void run() {
      try {
         System.out.println(new Date().toString() + " LockA 开始执行");
         while (true) {
            if (UnLockTest.a1.tryAcquire(1, TimeUnit.SECONDS)) {
               System.out.println(new Date().toString() + " LockA 锁住 obj1");
               if (UnLockTest.a2.tryAcquire(1, TimeUnit.SECONDS)) {
                  System.out.println(new Date().toString() + " LockA 锁住 obj2");
                  Thread.sleep(60 * 1000); // do something
               }else{
                  System.out.println(new Date().toString() + "LockA 锁 obj2 失败");
               }
            }else{
               System.out.println(new Date().toString() + "LockA 锁 obj1 失败");
            }
            UnLockTest.a1.release(); // 释放
            UnLockTest.a2.release();
            Thread.sleep(1000); // 马上进行尝试,现实情况下do something是不确定的
         }
      } catch (Exception e) {
         e.printStackTrace();
      }
   }
}
class LockBb implements Runnable {
   public void run() {
      try {
         System.out.println(new Date().toString() + " LockB 开始执行");
         while (true) {
            if (UnLockTest.a2.tryAcquire(1, TimeUnit.SECONDS)) {
               System.out.println(new Date().toString() + " LockB 锁住 obj2");
               if (UnLockTest.a1.tryAcquire(1, TimeUnit.SECONDS)) {
                  System.out.println(new Date().toString() + " LockB 锁住 obj1");
                  Thread.sleep(60 * 1000); // do something
               }else{
                  System.out.println(new Date().toString() + "LockB 锁 obj1 失败");
               }
            }else{
               System.out.println(new Date().toString() + "LockB 锁 obj2 失败");
            }
            UnLockTest.a1.release(); // 释放
            UnLockTest.a2.release();
            Thread.sleep(10 * 1000); // 这里只是为了演示,所以tryAcquire只用1秒,而且B要给A让出能执行的时间,否则两个永远是死锁
         }
      } catch (Exception e) {
         e.printStackTrace();
      }
   }
}
Tue May 05 10:59:13 CST 2015 LockA 开始执行
Tue May 05 10:59:13 CST 2015 LockB 开始执行
Tue May 05 10:59:13 CST 2015 LockB 锁住 obj2
Tue May 05 10:59:13 CST 2015 LockA 锁住 obj1
Tue May 05 10:59:14 CST 2015LockB 锁 obj1 失败
Tue May 05 10:59:14 CST 2015LockA 锁 obj2 失败
Tue May 05 10:59:15 CST 2015 LockA 锁住 obj1
Tue May 05 10:59:15 CST 2015 LockA 锁住 obj2
  1. synchronized 与 Lock 的对比
    Lock是显式锁(手动开启和关闭锁,别忘记关闭锁)synchronized是隐式锁,出了作用域自动释放
  2. Lock只有代码块锁,synchronized有代码块锁和方法锁使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
  3. 优先使用顺序:
    Lock > 同步代码块(已经进入了方法体,分配了相应资源)> 同步方法(在方
    法体之外)

线程通讯

线程通信的目标是使线程间能够互相发送信号。另一方面,线程通信使线程能够等待其他线程的信号。

线程的通信方式

  1. volatile
  2. Wait/Notify机制
  3. join方式
  4. threadLocal
  5. CountDownLatch 并发工具
  6. CyclicBarrier 并发工具

volatile

public class Volatile implements Runnable {
  private static volatile Boolean flag = true;

  @Override
  public void run() {
    while (flag) {
      System.out.println(Thread.currentThread().getName() + " - 执行");
    }
    System.out.println("线程结束");
  }

  public static void main(String[] args) {
    Thread t = new Thread(new Volatile());
    t.start();
    try {
      Thread.sleep(5);
      flag = false;
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}
Thread-0 - 执行
Thread-0 - 执行
Thread-0 - 执行
Thread-0 - 执行
Thread-0 - 执行
线程结束

**WaitNotify **


public class WaitNotify {
  // 状态锁
  private static Object lock = new Object();
  private static Integer i = 0;

  public void odd() {
    while (i < 10) {
      synchronized (lock) {
        if (i % 2 == 1) {
          System.out.println(Thread.currentThread().getName() + " - " + i);
          try {
            Thread.sleep(1000);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          i++;
          lock.notify();
        } else {
          try {
            lock.wait();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }
    }
  }

  public void even() {
    while (i < 10) {
      synchronized (lock) {
        if (i % 2 == 0) {
          System.out.println(Thread.currentThread().getName() + " - " + i);
          try {
            Thread.sleep(1000);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          i++;
          lock.notify();
        } else {
          try {
            lock.wait();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }
    }
  }

  public static void main(String[] args) {

    WaitNotify waitNotify = new WaitNotify();

    Thread t1 = new Thread(() -> waitNotify.odd(), "线程1");
    Thread t2 = new Thread(() -> waitNotify.even(), "线程2");

    t1.start();
    t2.start();
  }
}

join

package threadCommunication;
 
public class JoinTest extends Thread {
    @Override
    public void run() {
        try {
            int sleepTime = (int) (Math.random() * 1000);
            System.out.println(sleepTime);
            Thread.sleep(sleepTime);
            System.out.println("JoinTest end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
 
    public static void main(String[] args) throws InterruptedException {
        JoinTest j = new JoinTest();
        j.start();
        j.join();//当前线程main等待线程对象(j)销毁
        System.out.println("main end");
    }

threadLocal

package sync; 
public class SequenceNumber { 
 // 定义匿名子类创建ThreadLocal的变量 
 private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>() { 
 // 覆盖初始化方法 
 public Integer initialValue() { 
 		return 0; 
 	} 
 }; 
 // 下一个序列号 
 public int getNextNum() { 
     seqNum.set(seqNum.get() + 1); 
     return seqNum.get(); 
 } 
 private static class TestClient extends Thread { 
     private SequenceNumber sn; 
     public TestClient(SequenceNumber sn) { 
     this.sn = sn; 
 } 
 // 线程产生序列号 
 public void run() { 
     for (int i = 0; i < 3; i++) { 
         System.out.println("thread[" + Thread.currentThread().getName() + "] sn[" + 			sn.getNextNum() + "]"); 
         } 
 	} 
 } 
 /** 
 * @param args 
 */ 
 public static void main(String[] args) { 
     SequenceNumber sn = new SequenceNumber(); 
     // 三个线程产生各自的序列号 
     TestClient t1 = new TestClient(sn); 
     TestClient t2 = new TestClient(sn); 
     TestClient t3 = new TestClient(sn); 
     t1.start(); 
     t2.start(); 
     t3.start(); 
 } 
}
thread[Thread-1] sn[1] 
thread[Thread-1] sn[2] 
thread[Thread-1] sn[3] 
thread[Thread-2] sn[1] 
thread[Thread-2] sn[2] 
thread[Thread-2] sn[3] 
thread[Thread-0] sn[1]
thread[Thread-0] sn[2] 
thread[Thread-0] sn[3]

**CountDownLatch **CountDownLatch可以代替wait/notify的使用,并去掉synchronized

import java.util.concurrent.CountDownLatch;

public class CountDown {
  private static Integer i = 0;
  final static CountDownLatch countDown = new CountDownLatch(1);

  public void odd() {
    while (i < 10) {
      if (i % 2 == 1) {
        System.out.println(Thread.currentThread().getName() + " - " + i);
        try {
          Thread.sleep(1000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        i++;
        countDown.countDown();
      } else {
        try {
          countDown.await();
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }
  }

  public void even() {
    while (i < 10) {
      if (i % 2 == 0) {
        System.out.println(Thread.currentThread().getName() + " - " + i);
        try {
          Thread.sleep(1000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        i++;
        countDown.countDown();
      } else {
        try {
          countDown.await();
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }
  }

  public static void main(String[] args) {

    CountDown countDown = new CountDown();

    Thread t1 = new Thread(() -> countDown.odd(), "线程1");
    Thread t2 = new Thread(() -> countDown.even(), "线程2");

    t1.start();
    t2.start();
  }
}

CyclicBarrier

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierTest {
  public static void main(String[] args) {
    CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
    new Thread(() -> {
      System.out.println(Thread.currentThread().getName() + ": 准备...");
      try {
        cyclicBarrier.await();
      } catch (InterruptedException e) {
        e.printStackTrace();
      } catch (BrokenBarrierException e) {
        e.printStackTrace();
      }
      System.out.println("全部启动完毕!");
    }, "线程1").start();

    new Thread(() -> {
      System.out.println(Thread.currentThread().getName() + ": 准备...");
      try {
        cyclicBarrier.await();
      } catch (InterruptedException e) {
        e.printStackTrace();
      } catch (BrokenBarrierException e) {
        e.printStackTrace();
      }
      System.out.println("全部启动完毕!");
    }, "线程2").start();

    new Thread(() -> {
      System.out.println(Thread.currentThread().getName() + ": 准备...");
      try {
        cyclicBarrier.await();
      } catch (InterruptedException e) {
        e.printStackTrace();
      } catch (BrokenBarrierException e) {
        e.printStackTrace();
      }
      System.out.println("全部启动完毕!");
    }, "线程3").start();
  }
}
线程3: 准备...
线程2: 准备...
线程1: 准备...
全部启动完毕!
全部启动完毕!
全部启动完毕!

线程池

经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。

ExecutorService 和 Executors

  1. ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
    void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执
    行Runnable

  2. Future submit(Callable task):执行任务,有返回值,一般又来执行
    Callable

  3. void shutdown() :关闭连接池

Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池

13.注解

Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。

作用

不是程序本身 , 可以对程序作出解释.(这一点和注释(comment)没什么区别)

可以被其他程序(比如:编译器等)读取.

可以附加在package , class , method , field 等上面 , 相当于给他们添加了额外的辅助信息

我们可以通过反射机制实现对这些元数据的访问

格式

注解是以"@注释名"在代码中存在的

还可以添加一些参数值 , 例如:@SuppressWarnings(value="unchecked")

内置注解

image-20210809164852100
Java 定义了一套注解,共有 10 个,java7之前3 个在 java.lang 中, 4 个在 java.lang.annotation 中,后续增加三个

作用在代码的注解( java.lang )是
@Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
@Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
@SuppressWarnings - 指示编译器去忽略注解中声明的警告。
作用在其他注解的注解(或者说 元注解)是( java.lang.annotation)注解
@Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
@Documented - 标记这些注解是否包含在用户文档中。
@Target - 标记这个注解应该是哪种 Java 成员。
@Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)
java7之后增加的注解
@SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
@FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
@Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
package com.annotation;
//测试内置注解
import java.util.ArrayList;
import java.util.List;
//所有类默认继承Object类
public class Test1 extends Object {
    //@Override 表示方法重写
    //--> 查看JDK帮助文档
    //--> 测试名字不同产生的效果
    @Override
    public String toString() {
    	return super.toString();
    }
     //方法过时了, 不建议使用 , 可能存在问题 , 并不是不能使用!
    //--> 查看JDK帮助文档
    @Deprecated
    public static void stop(){
    	System.out.println("测试 @Deprecated");
    }
    //@SuppressWarnings 抑制警告 , 可以传参数
    //--> 查看JDK帮助文档
    //查看源码:发现 参数类型 和 参数名称 , 并不是方法!
    @SuppressWarnings("all")
    public void sw(){
   	 List list = new ArrayList();
    }
    public static void main(String[] args) {
   	 stop();
    }
}   

元注解

package com.annotation;
import java.lang.annotation.*;
//测试元注解
public class Test2 {
@MyAnnotation
public void test(){
}
}
//定义一个注解
@Target(value = {ElementType.METHOD,ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@Inherited
@Documented
@interface MyAnnotation{
}

自定义注解

  1. 使用 @interface自定义注解时 , 自动继承了java.lang.annotation.Annotation接口
    @ interface用来声明一个注解 , 格式 : public @ interface 注解名

  2. 其中的每一个方法实际上是声明了一个配置参数.
    方法的名称就是参数的名称.

  3. 返回值类型就是参数的类型 ( 返回值只能是基本类型,Class , String , enum ).
    可以通过default来声明参数的默认值

  4. 如果只有一个参数成员 , 一般参数名为value

  5. 注解元素必须要有值 , 我们定义注解元素时 , 经常使用空字符串,0作为默认值 .

注解参数的可支持数据类型:
1.所有基本数据类型(int,float,boolean,byte,double,char,long,short)
2.String类型
3.Class类型
4.enum类型
5.Annotation类型
6.以上所有类型的数组

package annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 水果名称注解
 * @author peida
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
    String value() default "";
}
package annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 水果颜色注解
 * @author peida
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
    /**
     * 颜色枚举
     * @author peida
     *
     */
    public enum Color{ BULE,RED,GREEN};
    
    /**
     * 颜色属性
     * @return
     */
    Color fruitColor() default Color.GREEN;

}
package annotation;

import annotation.FruitColor.Color;

public class Apple {
    
    @FruitName("Apple")
    private String appleName;
    
    @FruitColor(fruitColor=Color.RED)
    private String appleColor;
    
    
    
    
    public void setAppleColor(String appleColor) {
        this.appleColor = appleColor;
    }
    public String getAppleColor() {
        return appleColor;
    }
    
    
    public void setAppleName(String appleName) {
        this.appleName = appleName;
    }
    public String getAppleName() {
        return appleName;
    }
    
    public void displayName(){
        System.out.println("水果的名字是:苹果");
    }
}

//设置默认值
package annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 水果供应者注解
 * @author peida
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
    /**
     * 供应商编号
     * @return
     */
    public int id() default -1;

    /**
     * 供应商名称
     * @return
     */
    public String name() default "";

    /**
     * 供应商地址
     * @return
     */
    public String address() default "";
}

14.反射

反射机制

image-20210809170425919

Java的反射机制的实现要借助于4个类:class,Constructor,Field,Method;
其中class代表的时类对 象,Constructor-类的构造器对象,Field-类的属性对象,Method-类的方法对象。通过这四个对象我们可以粗略的看到一个类的各个组 成部分。

获取类的方法

//调用运行时类本身的.class属性
Class clazz = String.class;

//通过运行时类的对象获取
Person p = new Person();

Class clazz = p.getClass();

//通过Class的静态方法获取:体现反射的动态性
String className = “java.util.commons”;

Class clazz = Class.forName(className);

//通过类的加载器
String className = “java.util.commons”;

ClassLoader classLoader = this.getClass().getClassLoader();

Class clazz = classLoader.loadClass(className);

得到构造器的方法

Constructor getConstructor(Class[] params) -- 获得使用特殊的参数类型的公共构造函数, 
 
Constructor[] getConstructors() -- 获得类的所有公共构造函数 
 
Constructor getDeclaredConstructor(Class[] params) -- 获得使用特定参数类型的构造函数(与接入级别无关) 
 
Constructor[] getDeclaredConstructors() -- 获得类的所有构造函数(与接入级别无关)

获取字段

Field getField(String name) -- 获得命名的公共字段 
 
Field[] getFields() -- 获得类的所有公共字段 
 
Field getDeclaredField(String name) -- 获得类声明的命名的字段 
 
Field[] getDeclaredFields() -- 获得类声明的所有字段

获取方法的信息

Method getMethod(String name, Class[] params) -- 使用特定的参数类型,获得命名的公共方法 
 
Method[] getMethods() -- 获得类的所有公共方法 
 
Method getDeclaredMethod(String name, Class[] params) -- 使用特写的参数类型,获得类声明的命名的方法 
 
Method[] getDeclaredMethods() -- 获得类声明的所有方法

通过 Class 类获取成员变量、成员方法、接口、超类、构造方法等

package com.ys.reflex;
public class Person {
    //私有属性
    private String name = "Tom";
    //公有属性
    public int age = 18;
    //构造方法
    public Person() {
    }
    //私有方法
    private void say(){
        System.out.println("private say()...");
    }
    //公有方法
    public void work(){
        System.out.println("public work()...");
    }
}
//获得类完整的名字
String className = c2.getName();
System.out.println(className);//输出com.ys.reflex.Person

//获得类的public类型的属性。
Field[] fields = c2.getFields();
for(Field field : fields){
   System.out.println(field.getName());//age
}

//获得类的所有属性。包括私有的
Field [] allFields = c2.getDeclaredFields();
for(Field field : allFields){
    System.out.println(field.getName());//name    age
}

//获得类的public类型的方法。这里包括 Object 类的一些方法
Method [] methods = c2.getMethods();
for(Method method : methods){
    System.out.println(method.getName());//work waid equls toString hashCode等
}

//获得类的所有方法。
Method [] allMethods = c2.getDeclaredMethods();
for(Method method : allMethods){
    System.out.println(method.getName());//work say
}

//获得指定的属性
Field f1 = c2.getField("age");
System.out.println(f1);
//获得指定的私有属性
Field f2 = c2.getDeclaredField("name");
//启用和禁用访问安全检查的开关,值为 true,则表示反射的对象在使用时应该取消 java 语言的访问检查;反之不取消
f2.setAccessible(true);
System.out.println(f2);

//创建这个类的一个对象
Object p2 =  c2.newInstance();
//将 p2 对象的  f2 属性赋值为 Bob,f2 属性即为 私有属性 name
f2.set(p2,"Bob");
//使用反射机制可以打破封装性,导致了java对象的属性不安全。
System.out.println(f2.get(p2)); //Bob

//获取构造方法
Constructor [] constructors = c2.getConstructors();
for(Constructor constructor : constructors){
    System.out.println(constructor.toString());//public com.ys.reflex.Person()
}

反射方法执行

public class Apple {

    private int price;

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    public static void main(String[] args) throws Exception{
        //正常的调用
        Apple apple = new Apple();
        apple.setPrice(5);
        System.out.println("Apple Price:" + apple.getPrice());
        //使用反射调用
        Class clz = Class.forName("com.wyl.api.Apple");
        Method setPriceMethod = clz.getMethod("setPrice", int.class);
        Constructor appleConstructor = clz.getConstructor();
        Object appleObj = appleConstructor.newInstance();
        setPriceMethod.invoke(appleObj, 14);
        Method getPriceMethod = clz.getMethod("getPrice");
        System.out.println("Apple Price:" + getPriceMethod.invoke(appleObj));
    }
}
posted @ 2021-08-10 14:01  王延领  阅读(4264)  评论(29编辑  收藏  举报