Java上篇
基础语法
public class Hello {
public static void main(String[] args) {
System.out.println("Hello,World!");
}
}
- 一个Java源文件中可以声明多个class。但是,最多只能有一个类可以声明为public。而且声明为public类的类名必须与源文件名相同。
- 程序的入口是main方法,变量名args可以改成其他的,[]可以写到args后。
命令行编译
javac java文件名(含后缀)# 生成字节码文件
java 类名(不含后缀)# 运行
注释
单行注释、多行注释
文档注释:
(需要public class)
/**
@author zhangsan
@version v1.0
这是我的第一个java程序
*/
/**
这是main函数
*/
命令行运行javadoc:
javadoc -d 取个名字 -author -version 文件名(含后缀)
//文件名与类名要相同
数据类型
整型
byte:1字节 short int long
声明long型的变量,必须以 l 或 L 结尾
整型默认为int
二进制:以0b或0B开头
八进制:以0开头
十六进制:以0x或0X开头
浮点
float型数据,后加 f 或 F
浮点数默认是double
字符
char(1字符=2字节)
byte、short、char之间(或与自己)计算,结果类型为int
String
String可以与8种基本类型数据进行+运算
运算
取模
取模的结果正负与被模数相同
赋值
可以连续赋值
short t = 10;
t = t + 2; //编译不通过
t += 2; //编译通过,这里的2也是short
逻辑运算
&
:与&&
:短路与:如果前面是false,后面就不看了|
:或||
:短路或
位运算
&|~
:与或非>>>
:逻辑右移>>>
:算数右移
Scanner类
// 1.导入包:
import java.util.Scanner;
// 2.Scanner实例化
Scanner scan = new Scanner(System.in);
// 3.调用方法
int num = scan.nextInt();
String name = scan.next(); //Scanner只能获取字符串
//获取字符 char c = name.charAt(0);
double weight = scan.nextDouble();
boolean b = scan.nextBoolean();
//如果输入的类型与要求的类型不匹配,会出现InputMisMathchException异常
break/continue标签
label:for(){
for(){
break label;
continue label;
}
}
一维数组
// 声明和初始化
int[] ids; //声明
ids = new int[]{1,2,3,4}; //静态初始化
int[] ids = new int[]{1,2,3,4};
int[] ids = {1,2,3,4};
String[] names = new String[5]; //动态初始化
// 数组长度
names.length
ids.length
// 默认初始化值
整型:0
浮点:0.0
char:数值为0
boolean:false
引用类型(String):null
多维数组
// 声明和初始化
int[][] arr1 = new int[][]{{1,2,3},{4,5},{6,7,8}}; //静态
String[][] arr2 = new String[3][2];
String[][] arr3 = new String[3][]; //动态
arr3[0] = new String[4]; //再单独申请第二维空间
// 长度
arr1.length //= 3;
arr1[0].length //= 3;
arr1[1].length //= 2;
// 默认初始化值
arr1[0] = 地址
arr1[0][0] = 0; //浮点,字符,布尔等同上
arr3[1] = null; //因为还没有申请第二维空间
arr3[1][0]; //报错
Arrays类
// 在java.util.Arrays包下
int[] arr1 = new int[]{1,2,3,4}
int[] arr2 = new int[]{1,2,3,4}
boolean isEquals = Arrays.equals(arr1,arr2); //比较是否相等
System.out.println(Arrays.toString(arr1)); //输出数组信息
Arrays.fill(arr1,10); //填充
Arrays.sort(arr2); //排序(升序)
int index = Arrays.binarySearch(arr2,3); //二分查找
快捷键
ctrl + 1 // 快速修复
ctrl + shift + O // 批量导包
ctrl + shift + / // 多行注释
ctrl + alt + down 或 ctrl + alt + up // 复制指定行的代码
ctrl + d // 删除指定行的代码
alt + up 或 alt + down // 上下移动代码
shift + enter // 切换到下一行代码空位
ctrl + shift + enter // 切换到上一行代码空位
ctrl + shift + t // 搜索源码
alt + left 或 alt + right // 退回到前一个/进入到后一个编辑页面
ctrl + t // 查看继承树
ctrl + shift + f // 格式化代码
ctrl + O // 显示当前类的结构、方法、属性,并支持搜索
alt + shift + r // 批量修改指定的变量名、方法名、类名等
ctrl + shift + x/y // 全部变为大写/小写
alt + shift + s // 快速生成getter/setter/构造器
ctrl + alt + g // 查看指定结构使用过的地方
类与对象
属性与局部变量
- 属性有默认值(同之前数组的默认值)
- 局部变量没有默认值,使用之前必须赋值
内存解析
匿名对象
// 只创建,但是不赋给变量;匿名对象只能调用一次。
new Phone().playGames("aaa");
new Phone().showPrice();
mall.show(new Phone()); //传入一个匿名类
可变个数形参
可变参数必须写在参数列表最后
main() {
test.show("hello");
test.show("hello","world");
test.show();
}
public void show(String ... strs) { //可变形参(类似于传入数组)
for(int i=0;i < strs.length;i++) {
syso(strs[i]);
}
}
权限修饰符
修饰符 | 类内部 | 同一个包 | 不同包的子类 | 同一个工程 |
---|---|---|---|---|
private | Yes | |||
(缺省) | Yes | Yes | ||
protected | Yes | Yes | Yes | |
public | Yes | Yes | Yes | Yes |
构造器(构造方法)
权限修饰符 类名(参数列表)
this关键字
表示:当前对象。可以使用this.属性
或this.方法
。
// 调用本类构造器:this(形参列表)
this();
this(age);
this("Tom",age);
// 在构造器中使用:this(形参列表) 必须声明首行(所以一个构造器里面只能使用一次this(形参列表))
// this直接表示当前对象
boy.marry(this); //Girl、Boy类都有marry方法,现在在Girl类中的marry方法中使用这一句,boy为Boy类的对象,this即表示当前的Girl类对象
package关键字
声明类或接口所属的包,声明在源文件首行
每
.
一次,就代表一层文件目录(如:com.atguigu.java1)同一包下不能命名同名的接口、类
import关键字
导入指定包下的类、接口,声明在包和类之间。
java.util.*
表示导入包下的所有结构java.lang
包下定义的可以直接使用,省略import- 本包下定义的也可以直接使用,省略import
- 如果使用了不同包下重名的类,则必须至少有一个类需要以全类名的方式使用(包名.类名)
- 如果使用XXX包子包下的结构,仍需import
- 导入指定类或接口中的静态结构
import static java.lang.System.*;
out.println(); //下面可以直接这样用
继承
不能多重继承。所有的类都继承Object
类。
重写
重写的方法与父类方法名称和参数列表完全相同
重写方法的权限修饰符不小于被重写的方法(父类的
private
方法子类无法重写,因为子类看不见,子类再写一遍相当于一个新的方法)返回值类型
- 父类是
void
,子类重写的方法只能是void
- 父类返回值是基本数据类型,子类重写的方法返回值类型必须相同
- 父类返回值是A类型,子类重写的方法返回值类型可以是A类或A类的子类
- 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
- 父类是
子类和父类中的同名同参数方法要么都声明为非
static
(重写),要么都声明为static
(不叫重写)
super关键字
调属性:当父类子类出现同名属性时,用
super.属性
调用父类的属性。调方法:对于重写的方法,用
super.方法
调用父类中的方法。调构造器
- 用
super(形参列表)
调用父类中的指定构造器。 super(形参列表)
必须用在子类构造器的首行,所以子类构造器的第一行this(形参列表)
或super(形参列表)
只能二选一。- 在构造器的首行没有显式的使用
this(形参列表)
或super(形参列表)
,则默认调用父类的空参构造器super()
。 - 在类的多个构造器中,至少有一个构造器使用了
super(形参列表)
调用父类的构造器。
- 用
子类对象的实例化过程
当我们通过子类的构造器创建子类的对象时,我们一定会直接或间接地调用其父类的构造器,进而调用父类的父类的构造器……直到调用Object类的空参构造器。正因为加载过所有的父类结构,所以子类对象才可以进行调用。
多态
对象的多态性
父类的引用指向子类的对象。
Person p = new Man();
多态的使用:虚拟方法调用
有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法;但在运行期,我们实际执行的是子类重写父类的方法。
编译,看左边;运行,看右边。
多态中的方法只有在运行时才能确定调用的是哪个方法(动态绑定)。这与重载不同,重载在编译时就确定了所调用的方法(静态绑定)。
注意事项
对象的多态性只适用于方法,不适用于属性。比如Person和子类Man中都定义了id,Person p = new Man()
,p.id是Person的id。
向上转型
就是指多态,Person p = new Man()
将子类对象赋值给了父类引用。
向下转型
使用强制类型转换,将父类对象强制转换为子类对象。如果转换为的对象不是原来new的对象,则会报ClassCastException
异常。
instanceof
a instanceof A
:判断对象a是否是A的实例。是则返回true,否则返回false。
如果a instanceof A == true
,A是B的子类,则a instanceof B 也 == true
。比如a instanceof Object == true
。
Object类
无属性。只声明了一个空参的构造器。
方法:clone()
, equals()
,
finalize()
由垃圾回收器在对象被销毁前调用,
getClass()
, hashCode()
,
toString()
, wait()
, notify()
,
notifyAll()
equals()
回顾==
使用在基本数据类型变量和引用数据类型变量中。如果是比较基本数据类型变量,则比较两个变量保存的数据是否相等(不一定要类型相同)。如果比较的是引用数据类型的变量,则比较两个对象的地址值是否相同(比如String,两个字面相等的String,但是地址不同,返回false)。
equals()方法的使用
只适用于引用数据类型。
Object类中equals()的定义
public boolean equals(Object obj) {
return (this == obj);
}
String、Date、File、包装类等都重写了Object中的equals()方法,比较的是两个对象的实体内容是否相同。
自己重写equals()方法的例子(一般都自动生成)
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj instanceof MyClass) {
MyClass mc = (MyClass) obj;
return this.age == mc.age && this.name.equals(mc.name);
}
return false;
}
toString()
当输出一个对象的引用时,实际上就是调用当前对象的
toString()
。Object类中toSting()的定义
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
- String、Date、File、包装类等都重写了Object中的toString()方法,输出的是实体内容。
包装类
相互转化
boolean的包装类Boolean的参数为String的构造函数,只要传入的字符串不是忽略大小写的
true
,都表示false
。Boolean b1 = new Boolean("true123"); //false Boolean b2 = new Boolean("TrUe"); //true
记得包装类的默认值是null
class A { boolean isMale; //默认为false Boolean isFemale; //默认为null }
JDK5.0以后支持包装类自动装箱和拆箱
int num1 = 10; Integer in = num1; int num2 = in; //假如一个函数的参数是Object obj,传入一个Integer对象,以下写法是对的 int score = (int) obj;/
关于包装类的面试题
Object o1 = true ? new Integer(1) : new Double(2.0);
System.out.println(o1);//输出1.0,因为编译的时候将Integer类型提升了
@Test
public void test3() {
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j); //false
//Integer内部定义了IntegerCache结构,IntegerCache中定义了Integer[],保存了从-128~127范围的整数。如果我们使用自动装箱的方式,给Integer赋值的范围在-128~127范围内时,可以直接使用数组中的元素,不用再去new了。目的: 提高效率
Integer m = 1;
Integer n = 1;
System.out.println(m == n); //true
Integer x = 128; //相当于new了一个Integer对象
Integer y = 128; //相当于new了一个Integer对象
System.out.println(x == y); //false
}
static关键字
修饰属性
属性按是否使用static修饰分为:静态属性(类变量)和非静态属性(实例变量)
实例变量:每个对象都有一套非静态属性。对象之间的非静态属性不会相互影响。
静态变量:多个对象共享同一个静态变量。当一个对象修改静态变量时,导致其他对象调用此静态变量时是修改过的。
静态变量随着类的加载而加载。早于对象创建。可以通过类.静态变量
的方式调用。由于类只会加载一次,静态变量在内存中也只有一份,存在方法区的静态域中。
修饰方法
随着类的加载而加载,可以通过类.静态方法
来调用。
静态方法中,只能调用静态方法或属性;非静态方法中,可以调用静态和非静态的方法和属性。
在静态方法内不能使用this、super关键字。
单例设计模式
饿汉式
对象加载时间过长。是线程安全的。
class Bank {
// 1.私有化类的构造器
private Bank() {
}
// 2.内部创建类的对象,此对象也必须是静态的
private static Bank instance = new Bank();
// 3.提供公共的方法,返回类的对象
public static Bank getInstance() {
return instance;
}
}
懒汉式
延迟对象的创建。以下写法是线程不安全的。
class Bank {
// 1.私有化类的构造器
private Bank() {
}
// 2.内部创建类的对象,此对象也必须是静态的,但不初始化
private static Bank instance = null;
// 3.提供公共的方法,返回类的对象
public static Bank getInstance() {
if (instance == null) {
instance = new Bank();
}
return instance;
}
}
线程安全的懒汉式:
class Bank {
// 1.私有化类的构造器
private Bank() {
}
// 2.内部创建类的对象,此对象也必须是静态的,但不初始化
private static Bank instance = null;
// 3.提供公共的方法,返回类的对象
public static Bank getInstance() {
if (instance == null) {
synchronized (Bank.class) {
if (instance == null) {
instance = new Bank();
}
}
}
return instance;
}
}
main方法
程序的入口
也是一个普通的静态方法(可以定义多个main函数,运行时要选择一个作为入口)
可以与控制台交互:String[] args,可以传参,参数都解析为String,参数加不加引号都行
代码块(初始化块)
用来初始化类、对象
只能使用static修饰:静态代码块,非静态代码块
静态代码块
- 内部可以有输出语句
- 随着类的加载而执行(只执行一次)
- 作用:初始化类的信息
- 如果定义了多个静态代码块,则按照声明的先后顺序执行
- 静态代码块的执行先于非静态代码块
- 只能在其中调用静态的属性和方法
static { }
非静态代码块
- 内部可以有输出语句
- 随着对象的创建而执行(每创建一个对象就执行一次)
- 作用:创建对象时,对对象的属性进行初始化
- 如果定义了多个非静态代码块,则按照声明的先后顺序执行
- 静态和非静态的属性和方法都可以调用
{ }
先加载静态,再加载非静态的。有继承关系的时候先加载父类的。(由父及子,静态先行)
main方法虽然是程序的入口,但它也是一个类的静态方法,执行main之前还要先执行该类的静态代码块,如果该类有父类的话,则父类的静态代码块更先执行。
对属性赋值的顺序
①默认初始化;②显式初始化;③构造器中初始化;④通过对象初始化;⑤在代码块中赋值。
顺序为:①→②/⑤→③→④,2和5谁写在前面谁先执行。
final关键字
- 修饰类:此类不能被其他类继承。如String、System、StringBuffer类。
- 修饰方法:此方法不可以被重写。如getClass()。
- 修饰变量:此时的变量就成为常量。
- 修饰属性:可以在显示初始化、代码块、构造器中赋初值。
- 修饰局部变量:表示常量。尤其是使用final修饰形参时,表示此形参是一个常量,一旦传值以后,只能使用该形参而不能赋值。
抽象类
abstract
可以用来修饰类和方法。
修饰类(抽象类)
- 此类不能实例化
- 抽象类中也有构造器,便于子类实例化时调用
- 都会提供抽象类的子类,用于实例化。
修饰方法(抽象方法)
public abstract void fun();
只有方法声明,没有方法体
包含抽象方法的类一定是抽象类(因为抽象方法没有方法体,不能用类的对象去调用抽象方法,为了保证抽象方法不会被该类的对象调用,该类也必须是抽象类,不会被实例化);但是抽象类中也可以没有抽象方法
若子类重写了父类中所有的抽象方法,此子类方可实例化
若子类没有重写父类中所有的抽象方法,则此子类也是一个抽象类,需要使用
abstract
修饰
注意点
- 不能用来修饰属性、构造器
- 不能用来修饰私有方法、静态方法(父子类同名同参数的静态方法不算重写)、final方法、final类
抽象类的匿名子类
/* Person是抽象类,Worker是它的子类,method(Person p) */
// 非匿名的类,非匿名的对象
Worker worker = new Worker();
method(worker);
// 非匿名的类,匿名的对象
method(new Worker());
// 匿名的类,非匿名的对象:这个类没有名字,只是拿Person的名字代替了一下,重写了Person类的方法,将这个匿名子类赋给p
Person p = new Person() {
@Override
public void eat() {
......
}
};
method(p);
// 都匿名
method(new Person() {
@Override
public void eat() {
......
}
});
接口
接口的成员
JDK7及以前:只能定义全局常量(
public static final
或省略)、抽象方法(public abstract
或省略)JDK8及以后:还可以定义静态方法、默认方法
interface Flyable {
public static final int MAX_SPEED = 7900;
int MIN_SPEED = 1;
public abstract void fly();
void stop();
// JDK8之后
public static void method1() {
方法体
}
public default void method2() {
方法体
}
}
使用
通过类implements
来实现。实现了接口中的所有抽象方法,则该类可以实例化;否则仍为抽象类。
接口可以多继承。
接口的多态性
// 以下代码只是举例,忽略语法
interface USB
class Flash implements USB
class Printer implements USB
// 定义的是USB,实际传入的是实现类
public void transform(USB usb);
接口的匿名实现类对象
Computer com = new Computer();
// 1.非匿名类的非匿名对象
Flash flash = new Flash();
com.transform(flash);
// 2.非匿名类的匿名对象
com.transform(new Flash());
// 3.匿名类的非匿名对象
USB phone = new USB(){
@Override
@Override
};
com.transform(phone);
// 4.匿名类的匿名对象
com.transform(new USB(){
@Override
@Override
});
代理模式(Proxy)
被代理类想做某事,交给代理类帮它做(代理类可能还要做一些其他工作)。
interface NetWork {
void browse();
}
public class Test {
public static void main() {
// 本来是server想联网,现在把联网交给proxyServer代理去做(代理还做了其他的工作check)
// 这样server没有显示的browse,而是通过代理调用了自己的browse
Server server = new Server();
ProxyServer proxyServer = new ProxyServer(server);
proxyServer.browse();
}
}
// 被代理类
class Server implements NetWork {
@Override
public void browse() {
sout("服务器访问网络");
}
}
// 代理类
class ProxyServer implements NetWork {
private NetWork work;
public ProxyServer(NetWork work) {
this.work = work;
}
public void check() {
sout("联网之前检查");
}
@Override
public void browse() {
check();
work.browse();
}
}
工厂模式
简单工厂模式
class CarFactory {
public static Car getCar(String type) {
if (type.equals("奥迪")) {
return new Audi();
} else if (type.equals("比亚迪")) {
return new BYD();
} else {
return null;
}
}
}
工厂方法模式
interface Factory {
Car getCar();
}
class AudiFactory implements Factory {
public Audi getCar() {
return new Audi();
}
}
class BYDFactory implements Factory {
public BYD getCar() {
return new BYD();
}
}
面试题
interface A {
int x = 0;
}
class B {
int x = 1;
}
class C extends B implements A {
public void fun() {
sout(x); // 错误
sout(super.x); // 正确
sout(A.x); // 正确
}
}
JDK8接口新特性
静态方法只能通过接口名调用
默认方法通过实现类的对象调用,可以被重写
如果父类和实现的接口中声明了同名同参数方法,则调用父类中的方法
如果实现类实现了多个接口,而这多个接口定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下报错(接口冲突)。所以这种情况必须重写
// public可省略
interface Compare {
public static void method1() { // 静态方法
方法体
}
public default void method2() { // 默认方法
方法体
}
}
class A implements Compare{
public void method2() {
// 重写method2
}
public void test() {
method2(); //调用重写的method2
Compare.super.method2(); // 调用接口中的method2
}
}
内部类
- 成员内部类
- 局部内部类(方法、代码块、构造器内)
class Person {
// 局部内部类
Person() {
class A {
}
}
public void method() {
class B {
}
}
{
class C {
}
}
// 成员内部类
class D {
}
}
成员内部类
- 作为外部类的成员:可以调用外部类的结构;static可以修饰;可以被4种权限修饰
- 作为一个类:有属性、方法、构造器;final、abstract可以修饰
实例化成员内部类的对象
// Dog是Person类中的静态内部类
Person.Dog dog = new Person.Dog();
// Bird是Person类中的非静态内部类
Person p = new Person(); // 非静态得先有对象
Person.Bird bird = p.new Bird();
调用外部类结构
// 内部类和外部类的方法或属性不重名,直接调用
eat();
sout(name);
// 如果与外部类Person中的方法或属性重名
Person.this.eat();
sout(Person.this.name);
局部内部类
// 返回一个实现了Comparable接口的类的对象
public Comparable getComparable() {
// 方法一
class MyComparable implements Comparable {
@Override
public int compareTo(object o) {
return 0;
}
}
return new MyComparable();
// 方法二
return new Comparable() {
@Override
public int compareTo(object o) {
return 0;
}
};
}
异常
分类
java.lang.Throwable
java.lang.Error
java.lang.Exception
编译时异常(checked)
IOException
FileNotFoundException
ClassNotFoundException
运行时异常(uncheck)
NullPointerException
ArithmeticException
try-catch-finally
try {
// 可能出现异常的代码
} catch (异常类型1 变量名1) {
// 处理1
} catch (异常类型2 变量名2) {
// 处理2
}
......
finally {
// 一定会执行的代码
}
处理异常的常用方法
try {
} catch (Exception e) {
sout(e.getMessage());
e.printStackTrace();
}
finally
finally中声明的是一定会被执行的代码,即使catch中又出现异常了、try中有return语句,catch中有return语句。
像数据库连接、输入输出流、网络编程Socket等资源,JVM不能自动回收,需要手动释放,就写在finally中。
public int method() {
try {
int[] a = new int[10];
sout(a[10]);
return 1;
} catch (Exception e) {
return 2;
} finally {
sout("一定会执行"); //先执行这一句再返回
}
}
FileInputStream fis = null;
try {
File file = new File("hello.txt");
fis = new FileInputStream(file);
......
} catch (IOException e) {
......
} finally {
try {
if (fis != null) fis.close();
} catch (IOException e) {
......
}
}
throws
public void method() throws 异常类列表 {
}
- 子类重写方法抛出的异常不大于父类被重写方法抛出的异常。
throw
throw new RuntimeException();
自定义异常类
- 继承现有的异常结构:
RuntimeException
、Exception
。 - 提供
serialVersionUID
,标识 - 提供重载的构造器
class MyException extends RuntimeException {
static final long serialVersionUID = -642865491873126L;
public MyException() {
}
public MyException(String message) {
super(message);
}
}
JUnit单元测试
- 选中当前工程 -> 右键选择:build path -> add libraries -> JUnit 4
- 创建Java类,进行单元测试(单独建一个类就可以了)。要求:是public;提供公共无参构造器。
- 在此类中声明单元测试方法。要求:方法权限public;没有返回值;没有形参。
- 单元测试方法上需要声明注解:
@Test
。需要在单元测试类中import org.junit.Test
。 - 声明好单元测试方法以后,就可以在方法体内测试相关的代码。
- 写完代码以后,左键双击单元测试方法名,右键:run as -> JUnit Test
- 结果说明:
- 执行结果没有异常:绿条
- 执行结果出现异常:红条
import org.junit.Test
public class JUnitTest/*随便起的名*/ {
int num = 10;
@Test
public void testEquals() {
String s1 = "aaa";
String s2 = "aaa";
System.out.println(s1.equals(s2));
}
}
实际开发过程中直接在要测的类中写@Test
,可以自动导入JUnit和导包。
多线程
Thread类
常用方法
start()
run()
currentThread():静态方法,返回执行当前代码的线程
getName():获取当前线程的名字
setName():设置当前线程的名字
给主线程命名:在main线程中写:
Thread.currentThread().setName("主线程");
通过修改构造方法命名
public MyThread(String name) { super(name); }
yield():释放当前CPU的执行权(当然也可能释放之后CPU又把执行权分给它了)
join():在线程a中调用线程b的join方法,此时线程a就进入阻塞状态,直到b执行完a才结束阻塞
sleep(long millis):阻塞millis毫秒
isAlive() :当前线程是否存活
线程的优先级
优先级只是概率上的优先级,不是绝对的。
MAX_PRIORITY = 10
MIN_PRIORITY = 1
NORM_PRIORITY = 5 //默认
- getPriority():获取线程优先级
- setPriority():设置优先级
线程创建
方式一:继承Thread类
- 创建一个继承Thread类的子类
- 重写Thread类的run()方法
- 创建Thread类的子类对象
- 通过此对象调用start()方法(启动线程,调用当前线程的run())
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(i)
}
}
}
public class ThreadTest {
public static void main(String[] args) {
//一般创建方法
MyThread t1 = new MyThread();
t1.start();
//用匿名子类创建
new Thread() {
@Override
public void run() {
}
}.start();
}
}
方式二:实现Runnable接口
- 创建一个实现了Runnable接口的类
- 实现类去实现Runnable中的抽象方法run()
- 创建实现类对象
- 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
- 通过Thread类的对象调用start()
class MyThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(i)
}
}
}
public class ThreadTest {
public static void main(String[] args) {
//一般创建方法
MyThread mt = new MyThread();
Thread t1 = new Thread(mt);
t1.start();
}
}
方式三:实现Callable接口
//1.创建一个实现Callable的实现类
class MyThread implements Callable {
//2.实现call方法,将操作写在里面
@Override
public Object call() throws Exception {//可以有返回值,可以抛出异常
int sum = 0;
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0) {
sout(i);
sum += i;
}
}
return sum;
}
}
public class Main {
public static void main(String[] args) {
//3.创建Callable实现类的对象
MyThread mt = new MyThread;
//4.将Callable实现类作为参数传入FutureTask类的构造器
FutureTask ft = new FutureTask(mt);
//5.将FutureTask的对象作为参数传入Thread类的构造器,并start
new Thread(ft).start();
//6.可以获取Callable中call方法的返回值
Object sum = futureTask.get();//get返回的是call的返回值sum
sout(sum);
}
}
方式四:线程池
- 提高响应速度(减少创建线程的时间)
- 降低资源消耗(重复利用线程)
- 便于线程管理
//main方法
//1.提供指定线程数量的线程池(ExecutorsService是接口)
ExecutorsService service = Executors.newFixedThreadPool(10);
//可以设置线程池的属性
ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;//实现类
//然后就可以设置一些属性
service1.setCorePoolSize(5);
......
//2.执行指定的线程的操作。需要提供实现Runnable或Callable接口的实现类对象
service.execute(new MyThread());//适合使用于Runnable
service.submit();//适合使用于Callable
//3.关闭连接池
service.shutdown();
线程的生命周期
线程同步
方式一:同步代码块
synchronized(同步监视器) {
//需要被同步的代码(操作共享数据的代码)
}
/**
同步监视器(锁)
1.任何类的对象都可以充当锁
2.要求多个线程必须共用同一把锁!
2.1采用实现Runnable方式创建多线程锁可以为:this
2.2两种方式创建多线程锁可以为:类名.class(类也是对象,且是唯一的)
*/
方法二:同步方法
//在返回值前加synchronized
public synchronized void fun() {
}
//采用方式二创建,直接同步方法就可以了
//采用方式一创建,同步方法前面要加static,保证唯一
//非静态同步方法的同步监视器是:this
// 静态同步方法的同步监视器是:当前类本身
Lock锁(JDK5.0新增)
class Window implements Runnable {
//1.实例化ReentrantLock
private ReentrantLock lock = new ReentrantLock();
public void run() {
try {
//2.调用锁定方法
lock.lock();
//要同步的代码
} finally {
//3.调用解锁方法
lock.unlock();
}
}
}
线程通信
- 必须使用在同步代码块或同步方法中
- 调用者必须是同步代码块或同步方法中的同步监视器
wait();//阻塞当前线程并释放锁
notify();//唤醒被await的一个线程,如果有多个线程被await,就唤醒优先级高的线程
notifyAll();//唤醒所有的线程