一直觉得自己写的不是技术,而是情怀,一个个的教程是自己这一路走来的痕迹。靠专业技能的成功是最具可复制性的,希望我的这条路能让你们少走弯路,希望我能帮你们抹去知识的蒙尘,希望我能帮你们理清知识的脉络,希望未来技术之巅上有你们也有我。
Java 基础 整个基础班 day06 – day10 完整学习知识(代码+练习+打印结果)跟着手把手敲一遍就会
文章目录
day11
navite关键字
在java框架中,被navite修饰的方式,只有方法的声明没有实现,它的内部是C/C++实现。
例如:
Math.pow(2, 3);
final 常量 无法改变
final:最终的,不可更改的,它的用法有:
1、修饰类
表示这个类不能被继承,没有子类
final class Eunuch{//太监类
}
class Son extends Eunuch{//错误
}
2、修饰方法
表示这个方法不能被子类重写
class Father{
public final void method(){
System.out.println("father");
}
}
class Son extends Father{
public void method(){//错误
System.out.println("son");
}
}
3、声明常量
final修饰某个变量(成员变量或局部变量),表示它的值就不能被修改,即常量,常量名建议使用大写字母。
如果某个成员变量用final修饰后,没有set方法,并且必须初始化(可以显式赋值、或在初始化块赋值、实例变量还可以在构造器中赋值)
public class Test{
public static void main(String[] args){
final int MIN_SCORE = 0;
final int MAX_SCORE = 100;
}
}
class Chinese{
public static final String COUNTRY = "中华人民共和国";
private String name;
public Chinese( String name) {
super();
this.name = name;
}
public Chinese() {
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//final修饰的没有set方法
public static String getCountry() {
return COUNTRY;
}
}
Object类
创建一个类,如何自动生成setter getter方法
public class Teacher {
private String name;
private String age;
private String sex;
}

public class Teacher {
private String name;
private String age;
private String sex;
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
toString()
它的作用是自动打印当前类里面的属性信息

类
class Person{
private String name = "fenghanxu";
private int age = 18;
Person() {}
Person(String name, int age){
this.name = name;
this.age = age;
}
public void speak(){
System.out.println(name + "说:我今年" + age);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
操作
public class day01 {
public static void main(String[] args){
Person p1 = new Person();
System.out.println("p1" + p1);
}
}
getClass()
获取运行时内存,跟iOS的[person class];一样
Person p1 = new Person();
System.out.println("p1类型:" + p1.getClass());
finailze()
它的主要作用是在对象被垃圾回收器回收之前,执行一些清理操作
类
public class Teacher {
private String name;
private String age;
private String sex;
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("轻轻的我走了.......");
}
}
操作
public class day01 {
public static void main(String[] args){
Teacher t1 = new Teacher();
System.out.println("t1类型:" + t1.getClass());
t1 = null;//先至空
System.gc();//在通知垃圾回收期回收
}
}
hashCode()
返回当前对象的hash码
public class day01 {
public static void main(String[] args){
Teacher t1 = new Teacher();
System.out.println("t1 hashCode:" + t1.hashCode());
}
}
打印

类中的hashCode使用

equals()
跟iOS的isEqualsTo是一样的
开发应用:用于插入更新列表数据
java开发,有这样的一个类,有一个模型数组去存储每一个模型,然后我想插入一个新的模型到模型数组,前提条件是,这个模型要与模型数组里面的模型不同,如何插入前进行判断这个模型
类中生成equals方法。使用com+N生成

完整模型
import java.util.Objects;
public class Teacher {
private String name;
private String age;
private String sex;
// 构造函数
public Teacher(String name, String age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
// Getter 和 Setter 方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
// 重写 equals 方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Teacher teacher = (Teacher) o;
return Objects.equals(name, teacher.name) &&
Objects.equals(age, teacher.age) &&
Objects.equals(sex, teacher.sex);
}
// 重写 hashCode 方法
@Override
public int hashCode() {
return Objects.hash(name, age, sex);
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
操作
public class day01 {
public static void main(String[] args){
Teacher t1 = new Teacher("Alice", "30", "Female");
Teacher t2 = new Teacher("Bob", "35", "Male");
Teacher t3 = new Teacher("Alice", "30", "Female");
System.out.println("t1等于t2 ?:" + t1.equals(t2));
System.out.println("t1等于t2 ?:" + t1.equals(t3));
}
}
打印

抽象初识
这个知识点了解就好,为后面的接口用法作铺垫,相当于iOS的协议使用,只有声明,没有实现
注意:
1.继承抽象类必须实现抽象类的方法
创建抽象类
//抽象类
public abstract class Bird {
//抽象方法
public abstract void eat();
}
抽象类的使用
public class Bigbird extends Bird {
@Override
public void eat() {
System.out.println("jiao");
}
}
接口
相当于iOS的协议,但是跟iOS的规则是有区别的
接口:
- 1.支持方法的声明
- 2.支持方法的实现
- 3.支持常量(不支持属性)
- 4.支持遵守多接口
- 5.如果接口方法没有实现就是抽象方法,子类必须实现,如果接口方法有实现就是
default方法,可以选择实现(重写),如果接口是静态方法就不能重写,只能直接调用,如果是私有方法,就只能接口类自己内部调用
接口类
public interface LiveAble {
// 定义抽象方法
public abstract void eat();
public abstract void breathe();
//定义默认方法
public default void sleep(){
System.out.println("静止不动");
}
//定义静态方法
public static void drink(){
System.out.println("喝水");
}
}
子类
public class Plant implements LiveAble {
//重写/实现接口的抽象方法
@Override
public void eat() {
System.out.println("吸收营养");
}
//重写/实现接口的抽象方法
@Override
public void breathe(){
System.out.println("吸入二氧化碳呼出氧气");
}
//重写接口的默认方法
@Override
public void sleep() {
System.out.println("闭上眼睛睡觉");
}
}
操作
public class day01 {
public static void main(String[] args){
//创建实现类(子类)对象
Plant p = new Plant();
p.eat();
p.breathe();
p.sleep();
}
}
打印

多接口实现
定义多个接口:
interface A {
public abstract void showA();
public abstract void show();
}
interface B {
public abstract void showB();
public abstract void show();
}
定义实现类:
public class C implements A,B{
@Override
public void showA() {
System.out.println("showA");
}
@Override
public void showB() {
System.out.println("showB");
}
@Override
public void show() {
System.out.println("show");
}
}
多接口出现相同方法的实现问题
多接口出现相同方法,如果重写了就执行实现自己的方法,如果想执行给的接口方法通过super执行
接口一
public interface Study {
public default void sleep() {
System.out.println("study sleep");
}
}
接口二
public interface Work {
public default void sleep() {
System.out.println("Work sleep");
}
}
操作,实现接口方法
class Person implements Study, Work{
@Override
public void sleep() {
System.out.println("接口 睡觉");
Study.super.sleep();
Study.super.sleep();
}
public static void main(String[] args){
Person p = new Person();
p.sleep();
}
}
打印

学完接口之后,下面就开始介绍系统常见的接口方法
Comparable 类内部比较器
开发中不会用到,有封装好的函数调用更加方便,就像iOS的高阶函数的使用
内部是指,比较器要在类里面遵守接口,在类里面实现方法
Object类有Comparable这样的一个方法,遵守接口,实现方法进行类里面的属性比较
实现方法,比较规则是自己写的
类
class Student implements Comparable{
private int id;
private String name;
private int score;
public Student(int id, String name, int score) {
this.id = id;
this.name = name;
this.score = score;
}
//省略了构造器、get/set、toString等方法
@Override
public int compareTo(Object o) {
//这些需要强制,将o对象向下转型为Student类型的变量,才能调用Student类中的属性
Student stu = (Student) o;
if(this.score != stu.score){
return this.score - stu.score;
}else{//成绩相同,按照学号比较大小
return this.id - stu.id;
}
}
}
实现操作
public class TestComparable {
public static void main(String[] args) {
Student s1 = new Student(1,"张三",89);
Student s2 = new Student(2,"李四",89);
if(s1.compareTo(s2)>0){
System.out.println("s1>s2");
}else if(s1.compareTo(s2)<0){
System.out.println("s1<s2");
}else{
System.out.println("s1 = s2");
}
}
}
打印

day12
Comparator 外部比较器
开发中不会用到,有封装好的函数调用更加方便,就像iOS的高阶函数的使用
外部比较器是指,需要比较的类没有实现比较器接口,但是比较,可以创建一个比较的类去实现接口,需要比较的时候把类传递进去,实现比较
学生累没有实现比较器接口方法
class Student{
private String name;
private int score;
public Student(String name, int score) {
super();
this.name = name;
this.score = score;
}
public Student() {
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "Student [name=" + name + ", score=" + score + "]";
}
}
定制一个比较器类
class StudentScoreCompare implements Comparator{
@Override
public int compare(Object o1, Object o2) {
Student s1 = (Student) o1;
Student s2 = (Student) o2;
return s1.getScore() - s2.getScore();
}
}
操作
import java.util.Comparator;
public class TestComparator {
public static void main(String[] args) {
Student stu1 = new Student("张三",89);
Student stu2 = new Student("李四",78);
StudentScoreCompare ssc = new StudentScoreCompare();
if(ssc.compare(stu1, stu2)>0){
System.out.println(stu1 + ">" + stu2);
}else if(ssc.compare(stu1, stu2)<0){
System.out.println(stu1 + "<" + stu2);
}else{
System.out.println(stu1 + "=" + stu2);
}
}
}
枚举
枚举比较适用于固定范围的选择,例如:世界上就只有男和女
public class day01 {
public enum Day {
SUNDAY,
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY
}
public static void main(String[] args) {
Day today = Day.MONDAY;
switch (today) {
case MONDAY:
System.out.println("Today is MONDAY");
break;
case TUESDAY:
System.out.println("Today is TUESDAY");
break;
case WEDNESDAY:
System.out.println("Today is WEDNESDAY");
break;
case THURSDAY:
System.out.println("Today is THURSDAY");
break;
case FRIDAY:
System.out.println("Today is FRIDAY");
break;
case SATURDAY:
System.out.println("Today is SATURDAY");
break;
case SUNDAY:
System.out.println("Today is SUNDAY");
break;
}
}
}
枚举实现接口的用法
定义接口
public interface Activity {
void perform();
}
枚举实现接口
public enum Day implements Activity {
MONDAY {
@Override
public void perform() {
System.out.println("Working hard on Monday!");
}
},
TUESDAY {
@Override
public void perform() {
System.out.println("Continuing work on Tuesday.");
}
},
WEDNESDAY {
@Override
public void perform() {
System.out.println("Midweek, keep going!");
}
};
// 枚举常量必须实现接口的方法
@Override
public abstract void perform();
}
操作
public class EnumInterfaceExample {
public static void main(String[] args) {
Day today = Day.MONDAY;
today.perform(); // 输出: Working hard on Monday!
Day tomorrow = Day.TUESDAY;
tomorrow.perform(); // 输出: Continuing work on Tuesday.
}
}
包装类
包装类就是把基本数据类型封装成对象,然后通过对象点出方法进行方便使用,例如:
int a = Integer.parseInt("123");
下面是基本类型对应的包装类型
| 序号 | 基本数据类型 | 包装类 |
|---|---|---|
| 1 | byte | Byte |
| 2 | short | Short |
| 3 | int | Integer |
| 4 | long | Long |
| 5 | float | Float |
| 6 | double | Double |
| 7 | char | Character |
| 8 | boolean | Boolean |
| 9 | void | Void |
装箱与拆箱
装箱(Boxing):将基本数据类型转换为对应的包装类对象。
拆箱(Unboxing):将包装类对象转换为对应的基本数据类型。
Integer i = 4; // 自动装箱,相当于 Integer i = Integer.valueOf(4);
i = i + 5; // 自动拆箱,相当于 i.intValue() + 5,然后再自动装箱
包装类的一些API
基本数据类型和字符串之间的转换:
基本数据类型转字符串:
int a = 10;
String str = a + ""; // 方式一
String str = String.valueOf(a); // 方式二
字符串转基本数据类型:
int a = Integer.parseInt("123");
double d = Double.parseDouble("123.45");
boolean b = Boolean.parseBoolean("true");
数据类型的最大最小值:
Integer.MAX_VALUE; // int的最大值
Integer.MIN_VALUE; // int的最小值
字符转大小写:
Character.toUpperCase('a'); // 转为大写
Character.toLowerCase('A'); // 转为小写
整数转进制:
Integer.toBinaryString(10); // 转为二进制字符串
Integer.toHexString(10); // 转为十六进制字符串
Integer.toOctalString(10); // 转为八进制字符串
静态内部类
Java很少用到
意思就是类里面还有一个类,它可以通过静态调用
语法格式:
【修饰符】 class 外部类{
【其他修饰符】 static class 内部类{
}
}
举例
//外部类
class Outer{
private static int a = 1;
private int b = 2;
//内部类:静态
protected static class Inner{
static int d = 4;//可以
void inMethod(){
System.out.println("out.a = " + a);
// System.out.println("out.b = " + b);//错误的
}
static void inTest(){
System.out.println("out.a = " + a);
}
static void inFun(int a){
System.out.println("out.a = " + Outer.a);
System.out.println("local.a = " + a);
}
}
}
使用
public class TestInner{
public static void main(String[] args){
//创建静态内部类
Outer.Inner in= new Outer.Inner();
in.inMethod();
Outer.Inner.inTest();
Outer.Inner.inFun(3);
}
}
非静态内部类
Java很少用到
意思就是类里面还有一个类,它可以通过实例对象创建调用
语法格式
【修饰符】 class 外部类{
【修饰符】 class 内部类{
}
}
举例
//外部类
class Outer{
private static int a = 1;
private int b = 2;
//内部类
protected class Inner extends Father{
// static int d = 4;//错误
int b = 5;
void inMethod(){
System.out.println("out.a = " + a);
System.out.println("out.b = " + Outer.this.b);
System.out.println("in.b = " + b);
System.out.println("father.c = " + c);
}
}
public static void outMethod(){
// Inner in = new Inner();//错误的
}
public Inner getInner(){
return new Inner();
}
}
使用
public class TestInner{
public static void main(String[] args){
Outer out = new Outer();
Outer.Inner in= out.new Inner();
in.inMethod();
Outer.Inner inner = out.getInner();
inner.inMethod();
}
}
day13
局部内部类
Java很少用到
当我们在开发过程中,需要用到一个抽象类的子类的对象或一个接口的实现类的对象,而且只创建一个对象,而且逻辑代码也不复杂。那么我们原先怎么做的呢?
(1)编写类,继承这个父类或实现这个接口
(2)重写父类或父接口的方法
(3)创建这个子类或实现类的对象
例如:
协议类
public interface Runnable{
public abstract void run();
}
类实现协议
//声明接口实现类
public class MyRunnable implements Runnable{
public void run(){
while(true){
System.out.println("大家注意安全");
try
Thread.sleep(1000);
}catch(Exception e){
}
}
}
}
使用(以前的写法)
public class Test{
public static void main(String[] args){
//如果MyRunnable类只是在这里使用一次,并且只创建它的一个对象
//分开两个.java源文件,反而不好维护
Runnable target = new MyRunnable();
Thread t = new Thread("安全提示线程",target);
t.start();
}
}
使用匿名类写法
public class Test{
public static void main(String[] args){
//MyRunnable类只是在这里使用一次,并且只创建它的一个对象,那么这些写代码更紧凑,更好维护
Runnable target = new Runnable(){
public void run(){
while(true){
System.out.println("大家注意安全");
try
Thread.sleep(1000);
}catch(Exception e){
}
}
}
};
Thread t = new Thread("安全提示线程",target);
t.start();
}
}
注解
注解是给编译器看的
@OVerride 重写方法,该方法来自父类继承
@Deprecated 过时不是不能使用,只是不推荐使用
用于表示被标记的数据已经过时,不建议使用,但是他的可以使用了,只是现在有更加好的方法去替代它

如果自己创建一个类用@Deprecated修饰,显示会出现过时的写画线,看下面的图片

@SuppressWarnings 抑制警告
@SuppressWarnings({“unused”,“rawtypes”, “unchecked”})
如果创建出来一个对象,然后不去使用,会出现警告的字样,如果你觉得难看想消除该警告,可以使用@SuppressWarnings修饰消除它。

day14 异常
运行异常

编译异常
文件不存在

异常处理 try { } catch { }
public class day01 {
public static void main(String[] args){
int [] arr = {10, 20, 30};
try {
System.out.println(arr[10]);
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
}
System.out.println("Game Over");
}
}

举例1: 发生异常可以声明多个异常类型
import java.util.InputMismatchException;
import java.util.Scanner;
public class day01 {
public static void main(String[] args){
double sum = 0;
double score[] = new double[2];
try {
Scanner in = new Scanner(System.in);
for (int i = 0; i < score.length; i++) {
System.out.println("请输入第" + (i + 1) + "个学生的成绩");
double s = in.nextDouble();
score[i] = s;
sum += s;
}
} catch (ArrayIndexOutOfBoundsException | InputMismatchException e) {
System.out.println("发生了异常: " + e.getMessage());
}
System.out.println("第一个学生的成绩是: " + score[0] + ",总分是: " + sum + ",平均分是:" + sum/score.length);
}
}
打印

可以多重catch
try {
} catch (ArrayIndexOutOfBoundsException i) {
System.out.println("发生了异常: " + i.getMessage());
} catch (InputMismatchException e) {
System.out.println("发生了异常: " + e.getMessage());
}
finally
注意:
-
1.在程序没有发生异常时 有return 语句 要先执行finally 再执行返回操作
-
2.在catch语句块内 进行return 那么也要先执行 finally 再执行返回操作
-
3.如果在finally中存在 return,那么无论前面在那个位置 有return 都会返回finally中的return值。
finally 一般可用于输出关键信息
try{
}catch(...){
}finally{
无论try中是否发生异常,也无论catch是否捕获异常,也不管try和catch中是否有return语句,都一定会执行
}
实例代码
import java.util.InputMismatchException;
import java.util.Scanner;
public class day01 {
public static void main(String[] args){
double sum = 0;
double score[] = new double[2];
try {
Scanner in = new Scanner(System.in);
for (int i = 0; i < score.length; i++) {
System.out.println("请输入第" + (i + 1) + "个学生的成绩");
double s = in.nextDouble();
score[i] = s;
sum += s;
}
} catch (ArrayIndexOutOfBoundsException | InputMismatchException e) {
System.out.println("发生了异常: " + e.getMessage());
} finally {
System.out.println("finally 必须执行");
}
System.out.println("第一个学生的成绩是: " + score[0] + ",总分是: " + sum + ",平均分是:" + sum/score.length);
}
}
打印

throw 和 throws 抛出异常
throw 和 throws 抛出异常区别
| 关键字 | 作用 | 用法 | 示例 |
|---|---|---|---|
| throws | 方法签名 中声明异常,不处理,仅告知调用者 | 方法名() throws 异常类型 | public void test() throws IOException {} |
| throw | 方法体内部 抛出具体异常实例 | throw new 异常对象; | throw new IOException(“错误信息”); |
就是写法上的区别
throw

throws

throw 方法体内抛出异常
throw 的用法就是谁给异常我,我就把异常还给谁,谁调用我出现异常的,我就给回谁,然后我就在调用我出现异常的地方使用try…catch…就可以了,看下面两个形象的例子
看下面的图片45行代码出现异常,它就把代码跑给调用它的38行

例如:
假设我们有一个方法,用于检查年龄是否合法。如果年龄小于0或大于120,我们抛出一个自定义异常 IllegalAgeException。
// 自定义异常类
class IllegalAgeException extends Exception {
public IllegalAgeException(String message) {
super(message);
}
}
public class day01 {
public static void main(String[] args) {
try {
validateAge(-5); // 这里会抛出异常
} catch (IllegalAgeException e) {
System.out.println("捕获异常: " + e.getMessage());
}
try {
validateAge(130); // 这里也会抛出异常
} catch (IllegalAgeException e) {
System.out.println("捕获异常: " + e.getMessage());
}
try {
validateAge(25); // 这里不会抛出异常,但仍需处理
} catch (IllegalAgeException e) {
System.out.println("捕获异常: " + e.getMessage());
}
}
// 检查年龄的方法
public static void validateAge(int age) throws IllegalAgeException {
if (age < 0) {
throw new IllegalAgeException("年龄不能为负数: " + age);
} else if (age > 120) {
throw new IllegalAgeException("年龄不能超过120岁: " + age);
} else {
System.out.println("年龄合法: " + age);
}
}
}
打印

方法外抛出异常
public class day01 {
// 使用 throws 声明异常
public static void validateAge(int age) throws IllegalArgumentException {
if (age < 18) {
// 使用 throw 抛出异常
throw new IllegalArgumentException("年龄必须大于18岁");
}
System.out.println("年龄验证通过");
}
public static void main(String[] args) {
try {
validateAge(16);
} catch (IllegalArgumentException e) {
System.out.println("捕获异常: " + e.getMessage());
}
}
}
打印

异常方法的重写
意思就是父类有个抛出异常的方法,子类继承父类重写父类抛出异常的方法,需要注意是子类抛出的异常不能比父类抛出的异常大,看下面的异常关系和举例的例子,意思就是抛出异常范围可以平级或者缩小异常范围
异常关系图

class Parent {
public void check() throws Exception {
System.out.println("父类方法");
}
}
class Child extends Parent {
@Override
public void check() throws IllegalAgeException { // 只能抛出比 Exception 更具体的异常
System.out.println("子类方法");
}
}
自定义异常
自定义异常就是系统打印的信息并不能满足自己的需求,想通过自己的加工和特俗的逻辑处理显示出来。
// 自定义异常类:余额不足异常
class InsufficientFundsException extends Exception {
//InsufficientFundsException是继承Exception(并不是自定义)
public InsufficientFundsException(String message) {
super(message);
//对异常的内容进行加工,写自己想写,但是本例子没有写自己的逻辑进行特殊的处理,而是自己使用父类的语句,super(message);
}
}
// 银行账户类
class BankAccount {
private String accountHolder;
private double balance;
public BankAccount(String accountHolder, double balance) {
this.accountHolder = accountHolder;
this.balance = balance;
}
// 取款方法
public void withdraw(double amount) throws InsufficientFundsException {
if (amount > balance) {
throw new InsufficientFundsException("余额不足!当前余额:" + balance + ",尝试取款:" + amount);
}
balance -= amount;
System.out.println("取款成功!剩余余额:" + balance);
}
}
public class day01 {
public static void main(String[] args) {
BankAccount account = new BankAccount("张三", 500.0);
try {
account.withdraw(600); // 余额不足,抛出异常
} catch (InsufficientFundsException e) {
System.out.println("异常捕获:" + e.getMessage());
}
try {
account.withdraw(200); // 取款成功
} catch (InsufficientFundsException e) {
System.out.println("异常捕获:" + e.getMessage());
}
}
}
打印

day 15 多线程
多线程
什么是线程?
什么是进程?
什么是串行?
什么是并发?
获取当前线程
System.out.println(Thread.currentThread().getName());
创建多线程运行 - 方法一 - 继承Thread类
自定义线程类:
public class MyThread extends Thread {
//定义指定线程名称的构造方法
public MyThread(String name) {
//调用父类的String参数的构造方法,指定线程的名称
super(name);
}
/**
* 重写run方法,完成该线程执行的逻辑
*/
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName()+":正在执行!"+i);
}
}
}
测试类:
public class Demo01 {
public static void main(String[] args) {
//创建自定义线程对象
MyThread mt = new MyThread("新的线程!");
//开启新线程
mt.start();
//在主方法中执行for循环
for (int i = 0; i < 10; i++) {
System.out.println("main线程!"+i);
}
}
}
打印

创建多线程运行 - 方法二 - 实现Runnable接口
自定义线程类:
public class MyThread extends Thread {
//定义指定线程名称的构造方法
public MyThread(String name) {
//调用父类的String参数的构造方法,指定线程的名称
super(name);
}
/**
* 重写run方法,完成该线程执行的逻辑
*/
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName()+":正在执行!"+i);
}
}
}
测试类:
public class day01 {
public static void main(String[] args) {
//创建自定义线程对象
MyThread mt = new MyThread("新的线程!");
//开启新线程
mt.start();
//在主方法中执行for循环
for (int i = 0; i < 10; i++) {
System.out.println("main线程!"+i);
}
}
}
打印

匿名类创建多线程 - 方法三
继承 Thread 类
public class ThreadDemo {
public static void main(String[] args) {
// 使用匿名类继承 Thread 并重写 run 方法
Thread thread = new Thread() {
@Override
public void run() {
System.out.println("线程 1 正在运行:" + Thread.currentThread().getName());
}
};
thread.start();
}
}
实现 Runnable 接口(推荐)
public class RunnableDemo {
public static void main(String[] args) {
// 使用匿名类创建 Runnable 对象
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程 2 正在运行:" + Thread.currentThread().getName());
}
});
thread.start();
}
}
isAlive() 检测线程是否活跃
class MyThread extends Thread {
public void run() {
System.out.println(Thread.currentThread().getName() + " 正在运行...");
try {
Thread.sleep(2000); // 休眠2秒,模拟线程运行过程
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 运行结束");
}
}
public class day01 {
public static void main(String[] args) {
MyThread thread = new MyThread();
System.out.println("线程启动前,isAlive():" + thread.isAlive()); // false
thread.start();
System.out.println("线程启动后,isAlive():" + thread.isAlive()); // true
try {
Thread.sleep(3000); // 主线程等待足够时间,让子线程执行完
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程运行结束后,isAlive():" + thread.isAlive()); // false
}
}
打印

线程的优先级
获取线程的优先级+设置线程的优先级
public class day01 {
public static void main(String[] args) {
// 创建线程
Thread thread = new Thread(() -> {
System.out.println("子线程运行中...");
System.out.println("子线程优先级:" + Thread.currentThread().getPriority());
});
// 设置线程优先级
thread.setPriority(Thread.MAX_PRIORITY); // 设置为最高优先级 (10)
// 启动线程
thread.start();
// 主线程获取自身优先级
System.out.println("主线程优先级:" + Thread.currentThread().getPriority());
}
}
打印

join() 线程等待
它的作用是让调用 join() 方法的线程等待,直到该 Thread 对象执行完毕(即线程终止)采取执行下一个任务
有点想iOS GCD的调度组,执行A(接口获取数据) B(接口获取数据) 任务后才执行C任务(回来主线程刷新UI)
下面举例它的使用:
class MyThread extends Thread {
public void run() {
System.out.println(Thread.currentThread().getName() + " 开始执行");
try {
Thread.sleep(2000); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 执行完毕");
}
}
public class JoinExample {
public static void main(String[] args) {
Thread thread1 = new MyThread();
Thread thread2 = new MyThread();
thread1.start();
thread2.start();
try {
thread1.join(); // 主线程等待 thread1 执行完毕
thread2.join(); // 主线程等待 thread2 执行完毕
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程继续执行");
}
}
打印

volatile保证线程间的数据的可见性
在Java中,volatile 是一种关键字,用于保证线程之间的 可见性,它可以确保当一个线程修改了某个变量的值,其他线程可以立即看到这个值的变化。
可见性解释:
默认情况下,Java中的变量在不同线程之间是不可见的。也就是说,线程A对变量的修改,线程B可能无法立即看到。这是由于JVM可能对内存做优化,比如缓存或重排序等。volatile 关键字通过禁止对变量的值进行缓存,从而确保变量的最新值对于所有线程是可见的。
使用场景:
当多个线程共享某个变量时,可以通过volatile来保证这个变量的最新值被其他线程及时更新和读取。常用于标志位、状态变量等简单数据。
如何使用:
在声明变量时,使用volatile关键字。例如:
private volatile boolean flag = false;
示例:
class VolatileExample {
private static volatile boolean flag = false;
public static void main(String[] args) throws InterruptedException {
Thread writer = new Thread(() -> {
try {
// 模拟一些操作
Thread.sleep(1000);
flag = true; // 修改共享变量
System.out.println("Flag has been set to true.");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread reader = new Thread(() -> {
while (!flag) {
// 只要flag是false,就一直循环
}
System.out.println("Flag has been detected as true.");
});
writer.start();
reader.start();
writer.join();
reader.join();
}
}
解释:
- 在上面的代码中,
flag是一个共享的变量,writer线程修改了它的值,而reader线程则不断检查它的值。 - 由于
flag声明为volatile,所以当writer线程修改flag的值时,reader线程能够立即看到这个变化,从而跳出循环并打印消息。 - 如果没有
volatile,reader线程可能不会立刻看到writer线程对flag的修改。
注意:
volatile只保证可见性,并不能保证原子性。如果多个线程同时修改一个变量,可能会导致问题。在这种情况下,需要使用synchronized 或其他同步机制来保证原子性。
yield() 线程的礼让
在Java中,yield() 是 Thread 类的一个静态方法,表示线程的“礼让”行为。调用 yield() 方法的线程会暂停其执行并将 CPU 的使用权交给同优先级的其他线程,从而使得调度器有机会选择其他线程执行。它并不意味着当前线程立即被挂起或暂停,只是建议调度器放弃当前线程的 CPU 使用权。
如何使用 yield() 方法
在Java中,你可以直接在一个线程的执行过程中调用 Thread.yield() 来表示线程希望“让步”。代码示例如下:
public class day01 {
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("Thread 1 - " + i);
Thread.yield(); // 线程在这里让步
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("Thread 2 - " + i);
Thread.yield(); // 线程在这里让步
}
}
});
thread1.start();
thread2.start();
}
}
打印

注意点
- 1.并不是强制:yield() 只是建议当前线程让步,操作系统的线程调度器可能会忽略这个建议,当前线程有可能仍然继续执行。
- 2.不等同于休眠:与 sleep() 不同,yield() 只是暂停当前线程的执行并释放 CPU 使用权,而 sleep() 会让线程进入休眠状态,并在指定时间后自动恢复执行。
总结来说,Thread.yield() 用于让当前线程“礼让”给其他线程,但这并不是一种强制的行为,具体的执行效果取决于系统的调度机制。
stop()(非常不建议使用)
stop()会强制终止线程的执行,并可能导致资源没有被正确释放,从而引发各种问题
public class day01 {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
int count = 0;
while (true) {
System.out.println("Thread running: " + count++);
}
}
});
thread.start();
try {
Thread.sleep(1000); // 等待1秒钟
} catch (InterruptedException e) {
e.printStackTrace();
}
// 强制停止线程 (不推荐使用)
thread.stop(); // 弃用,不推荐
}
}
setDaemon(true)(守护线程.了解)
Thread.setDaemon(true)方法用于设置线程为守护线程。守护线程是在后台运行的线程,当所有非守护线程(用户线程)结束时,JVM会自动终止所有守护线程。守护线程通常用于执行一些后台任务,比如垃圾回收、定时任务等。
public class day01 {
public static void main(String[] args) {
Thread daemonThread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println("Daemon thread is running...");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
daemonThread.setDaemon(true); // 设置为守护线程
daemonThread.start();
try {
Thread.sleep(2000); // 主线程休眠2秒
} catch (InterruptedException e) {
e.printStackTrace();
}
// 主线程结束后,守护线程也会自动停止
System.out.println("Main thread finished");
}
}
注意: 在调用setDaemon(true)时,必须在start()方法之前设置。如果在start()方法之后设置,会抛出IllegalThreadStateException异常。
打印

线程安全
举例子:售票处,3个窗口(开启异步线程)同时卖60张票,结果出现出乱,卖多了
同步代码块解决
public class TicketSale {
private int tickets = 60; // 总共60张票
// 定义售票窗口
public void sellTicket() {
// 使用同步代码块确保线程安全
synchronized (this) {
if (tickets > 0) {
tickets--; // 卖出一张票
System.out.println(Thread.currentThread().getName() + " sold 1 ticket. Remaining tickets: " + tickets);
} else {
System.out.println("No tickets left.");
}
}
}
public static void main(String[] args) {
TicketSale ticketSale = new TicketSale();
// 定义3个售票窗口(线程)
Runnable sellTask = new Runnable() {
@Override
public void run() {
while (true) {
ticketSale.sellTicket();
try {
Thread.sleep(10); // 模拟销售过程中短暂的时间延迟
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
// 创建并启动3个线程
Thread window1 = new Thread(sellTask, "Window 1");
Thread window2 = new Thread(sellTask, "Window 2");
Thread window3 = new Thread(sellTask, "Window 3");
window1.start();
window2.start();
window3.start();
}
}
打印

解释:
synchronized代码块:synchronized (this)关键字用来锁定当前对象(this),确保在任何时刻只有一个线程能够进入该代码块,其他线程必须等待直到当前线程退出该块。- 票数操作:当一个线程进入同步代码块时,它会减少票数,并且在执行完成之前不会有其他线程可以操作票数,从而避免了超卖的情况。
- 模拟多个窗口:通过创建多个线程模拟3个售票窗口。
结果:
当3个线程同时尝试售票时,由于使用了synchronized,每次只有一个线程能够修改tickets变量,避免了卖多票的情况。
这种方式能有效解决线性安全问题,确保线程安全地操作共享资源。
同步方法解决
public class day01 {
private int count = 0;
// 通过 synchronized 关键字保证方法是线程安全的
public synchronized void increment() {
count++;
}
// 通过 synchronized 保证获取 count 的时候数据是最新的
public synchronized int getCount() {
return count;
}
public static void main(String[] args) {
day01 counter = new day01();
// 创建多个线程同时对 count 进行操作
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
};
// 创建两个线程执行任务
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
thread1.start();
thread2.start();
// 等待线程执行完
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 由于方法是同步的,最终 count 值应该是 2000
System.out.println("Final Count: " + counter.getCount());
}
}

横向比较:
上面两种方法的比较:
同步代码块 是在代码执行时仅对某个代码片段进行加锁,而 同步方法 是对整个方法加锁。同步代码块可以提高性能,因为它允许更精细地控制锁的范围,只锁住关键部分,而不是整个方法。它们的主要区别在于锁的粒度不同。
两种方法都属于:synchronized方法
解决线性安全还有很多方法:
| 方法 | 适用场景 | 线程安全 |
|---|---|---|
| synchronized | 代码块/方法同步 | ✅ |
| ReentrantLock | 更灵活的锁控制 | ✅ |
| volatile | 可见性(非原子性操作) | 🚫 |
| AtomicInteger | 计数器等简单操作 | ✅ |
| ThreadLocal | 线程独立变量 | ✅ |
| ConcurrentHashMap | 线程安全的Map | ✅ |
| CopyOnWriteArrayList | 读多写少的场景 | ✅ |
| ExecutorService | 线程池管理 | ✅ |
| ForkJoinPool | 并行计算 | ✅ |
| Semaphore | 并发访问控制 | ✅ |
单例模式
单例的写法有一下几种方式
| 方式 | 线程安全 | 懒加载 | 推荐程度 |
|---|---|---|---|
| 饿汉式 | ✅ | ❌ | ⭐⭐⭐ |
| 懒汉式(同步方法) | ✅ | ✅ | ⭐⭐ |
| 懒汉式(双重检查锁定) | ✅ | ✅ | ⭐⭐⭐ |
| 静态内部类 | ✅ | ✅ | ⭐⭐⭐⭐ |
| 枚举 | ✅ | ❌ | ⭐⭐⭐⭐⭐ |
示例:静态内部类(推荐,线程安全)
优点:类加载时不会初始化实例,只有调用 getInstance() 时才创建,避免了资源浪费,同时线程安全。
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
使用
public class day01 {
public static void main(String[] args) {
// 获取单例对象
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
// 打印对象地址,验证是否是同一个实例
System.out.println(singleton1);
System.out.println(singleton2);
// 判断两个对象是否相同
System.out.println(singleton1 == singleton2); // true
}
}
打印

示例:懒汉式(线程安全) - 用到的时候才实例化对象
public class Singleton {
private static Singleton instance;
// 私有构造方法,防止外部实例化
private Singleton() {}
// 提供全局访问点(双重检查锁定,保证线程安全)
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
public void showMessage() {
System.out.println("Hello, Singleton!");
}
}
使用
public class day01 {
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
singleton.showMessage();
}
}
打印

本章结束…
下一篇:
Java 基础 整个基础班 day16 – day20 完整学习知识(代码+练习+打印结果)跟着手把手敲一遍就会
跟着手把手敲一遍就会&spm=1001.2101.3001.5002&articleId=148952540&d=1&t=3&u=9c807cebdb134a0a9c9adfc4c3c23b85)
881

被折叠的 条评论
为什么被折叠?



