我的天啊一天又要过去了,没事的还有时间!!!
不要焦虑不要焦虑,事实证明只要我认真地投入进去一切都还是来得及的,代码多实操多复盘,别叽叽喳喳胡思乱想多多思考,有迷茫前害怕后的功夫已经可以学很多东西了,不许胡思乱想了快点学,,,对,加油,相信自己哦!!
---------------------------------------------------------------------------------------------------------------------------------
这是day2 写的,是的一般两天合起来更一次,一路生花~~~~❀~!!~!~!~!~!~!
加油加油加油!我的天啊期末考试慌死我了不会挂科的不会挂的不会的,,,吓鼠我了。。
pta还剩两道题目,记得看课了再做,做的太多了,今天要消化不良了。。。。。
1 设置和获取线程名称(小结)
Thread 类中设置和获取线程名称的方法
- void setName (String name):将此线程的名称更改为等于参数 name
- String getName ():返回此线程的名称
- 通过构造方法也可以设置线程名称
如何获取 main () 方法所在的线程名称?
- public static Thread currentThread ():返回对当前正在执行的线程对象的引用
2 线程调度
线程有两种调度模型~~~
- 分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片
- 抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些
Java 使用的是抢占式调度模型
假如计算机只有一个 CPU,那么 CPU 在某一个时刻只能执行一条指令,线程只有得到 CPU 时间片,也就是使用权,才可以执行指令。所以说多线程程序的执行是有随机性,因为谁抢到 CPU 的使用权是不一定的!!
Thread 类中设置和获取线程优先级的方法
- public final int getPriority ():返回此线程的优先级
- public final void setPriority (int newPriority):更改此线程的优先级
public final int getPriority()
:
调用这个方法时,会返回当前线程对象的优先级。
返回值是一个 int
类型,在 Java 里,线程优先级的取值范围通常是 1(最低优先级 )到 10(最高优先级 ),默认值是5,(不过不同的 JVM 实现可能在具体映射到底层系统优先级时有差异,但应用层代码遵循这个取值逻辑即可 。
通过获取优先级,我们可以在程序中判断线程当前的 “重要程度” 设定,用于一些调试、日志记录或者逻辑判断场景,比如打印线程优先级信息,辅助排查多线程执行顺序相关问题 。
public final void setPriority(int newPriority)
:
该方法用于更改当前线程的优先级~~~~
传入的参数 newPriority
需是在合法取值范围(1 - 10 )内的 int
值。
需要注意的是,设置优先级只是给线程调度一个 “提示”!!!!!,并非设置后高优先级线程就一定会立即执行、低优先级线程就完全无法执行,它受抢占式调度整体机制以及系统环境等影响,但合理设置优先级,能在多线程协作执行中,让关键任务线程更大概率获得 CPU 资源,提升程序整体的执行效率和业务逻辑的合理性,比如在处理紧急任务的线程上设置较高优先级,让其尽快得到执行 。
getPriority()
:
是 Thread
类中的一个实例方法,其作用是返回当前线程对象的优先级。线程的优先级是一个介于 1
(Thread.MIN_PRIORITY
)到 10
(Thread.MAX_PRIORITY
)之间的整数,默认优先级为 5
(Thread.NORM_PRIORITY
)。
package PTA_training.Thread_training;public class MyThreadDemo {public static void main(String[] args) {MyThread m1 = new MyThread("啊");MyThread m2 = new MyThread("ABC");// 设置 m1 的优先级为 10m1.setPriority(10);// 设置 m2 的优先级为 1m2.setPriority(1);// 启动线程m1.start();m2.start();}
}
package PTA_training.Thread_training;public class MyThread extends Thread {public MyThread(String name) {super(name);}@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(getName() + ":" + i);}}
}
3 线程控制
方法名 | 说明 |
---|---|
static void sleep(long millis) | 使当前正在执行的线程停留(暂停执行)指定的毫秒数 |
void join() | 等待这个线程死亡 |
void setDaemon(boolean on) | 将此线程标记为守护线程,当运行的线程都是守护线程时,Java 虚拟机将退出 |
4 消耗换行符的重要性
是的!在 Java 中使用 Scanner
时,输入流的换行符处理是非常容易出错的细节。以下是关于这个问题的深度解析和避坑指南,帮你彻底理解原理并避免类似问题:
一、为什么会出现换行符残留?
1. Scanner
的输入方法特性
nextInt()
/nextDouble()
等方法:
只会读取有效数据部分(如数字),不会消耗数据后的换行符(\n
),导致换行符残留在输入流中。nextLine()
方法:
会读取一行完整的输入,包括换行符(但会自动去除换行符)。如果输入流中残留换行符,nextLine()
会直接读取空字符串。
2. 具体场景示例
-
错误代码:
java
int n = scanner.nextInt(); // 输入:3(回车) String line = scanner.nextLine(); // 此时 line 是空字符串!
- 输入
3
后按下回车,nextInt()
读取3
,但残留\n
在输入流中。 nextLine()
会直接读取这个残留的\n
(视为空行),导致line
为空。
- 输入
-
正确做法:
在nextInt()
后手动消耗换行符:java
int n = scanner.nextInt(); scanner.nextLine(); // 主动读取并丢弃残留的 \n String line = scanner.nextLine(); // 此时正常读取下一行输入
二、常见输入组合的坑与解决方案
1. 场景 1:数字 + 字符串(行输入)
- 需求:先输入整数
n
,再输入n
行字符串(如姓名和年龄)。 - 错误代码:
java
int n = scanner.nextInt(); // 输入:3(回车) for (int i = 0; i < n; i++) {String line = scanner.nextLine(); // 第一次循环读取空字符串!// 导致 line.split(" ") 长度不足,引发 ArrayIndexOutOfBoundsException }
- 解决方案:
在nextInt()
后添加scanner.nextLine()
消耗换行符:java
int n = scanner.nextInt(); scanner.nextLine(); // 关键!消耗残留的 \n for (int i = 0; i < n; i++) {String line = scanner.nextLine(); // 正常读取每一行String[] parts = line.split(" ");// ... 处理逻辑 }
2. 场景 2:混合使用 next()
和 nextLine()
- 需求:先输入单词(如姓名),再输入整行文本。
- 错误代码:
java
String name = scanner.next(); // 输入:lily(回车) String desc = scanner.nextLine(); // 直接读取空字符串!
- 原因:
next()
读取到lily
后,残留的\n
被nextLine()
视为空行。 - 解决方案:
同样在next()
后添加scanner.nextLine()
消耗换行符(如果后续需要nextLine()
):java
String name = scanner.next(); scanner.nextLine(); // 消耗残留的 \n String desc = scanner.nextLine(); // 正常读取整行
三、终极避坑指南
1. 基本原则
- 如果先调用了
nextInt()
/next()
等非行读取方法,后续需要调用nextLine()
时,必须手动消耗残留的换行符。 - **********
- 养成习惯:在
nextInt()
/next()
后,立即添加scanner.nextLine()
来 “清理” 输入流。
2. 统一使用 nextLine()
处理输入
- 对于数字输入,先读取整行字符串,再转为数字:
java
String nStr = scanner.nextLine(); // 读取整行(含回车) int n = Integer.parseInt(nStr); // 转为整数
这样可以避免nextInt()
遗留换行符的问题。
3. 调试技巧
- 在怀疑输入流有残留时,添加
System.out.println("DEBUG: " + scanner.hasNextLine());
查看是否有未读取的行。 - 使用
scanner.hasNext()
或scanner.hasNextInt()
预判输入类型,确保逻辑正确。
四、总结:为什么这个坑容易被忽略?
- 换行符不可见:残留的
\n
不会在控制台显示,但会直接影响后续输入方法的行为。 - 惯性思维:以为输入方法会自动处理所有字符,但实际上
Scanner
的不同方法对换行符的处理策略不同。
记住这个口诀:非行读取后,必消换行符。
下次遇到类似问题时,先检查是否在 nextInt()
/next()
后遗漏了 nextLine()
的调用~
5 Express express;这是什么做法?划重点!
/*
7-5 快递计价器-2现需要编写一个简易快递计价程序。具体来说:1、抽象快递类Express,
其包含一个属性int weight表示快递重量(单位为kg),
一个方法getWeight()用于返回快递重量和一个抽象方法getTotal()用于计算快递运费。2、两个类继承Express,分别是:
(a)顺路快递SLExpress:计价规则为首重(1kg)12元,每增加1kg费用加2元。
(b)地地快递DDExpress:计价规则为首重(1kg)5元,每增加1kg费用加1元。3、Main:
接收用户通过控制台输入的N行信息,
自动计算每件快递的运费。输入样例:
6
SL 2
DD 2
SL 1
SL 1
SL 1
DD 3输入解释:
第1行n表示需要计算的快递件数
第2至n+1表示每个快递信息,
即选哪家快递公司 以及快递的重量(单位kg),
物品重量都是整数。输出样例:
SLExpress:14
DDExpress:6
SLExpress:12
SLExpress:12
SLExpress:12
DDExpress:7*/
import java.util.Scanner;
public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int n = scanner.nextInt();scanner.nextLine();for (int i = 0; i < n; i++) {String line = scanner.nextLine();String [] parts = line.split(" ");String name = parts[0];int weight = Integer.parseInt(parts[1]);Express express;//划重点,我卡住的地方if (name.equals("SL")){express = new SLExpress();express.weight = weight;System.out.println("SLExpress:"+ express.getTotal());} else if (name.equals("DD")) {express = new DDExpress();express.weight = weight;System.out.println("DDExpress:"+ express.getTotal());}else{continue;}}scanner.close();}
}
class SLExpress extends Express{@Overrideint getTotal(){if (weight > 1){return 12 + (weight - 1) * 2;}else {return 12;}}
}
class DDExpress extends Express{@Overrideint getTotal(){if (weight > 1){return 5 + (weight - 1 );}else {return 5;}}
}
abstract class Express {int weight;int getWeight(){return weight;}abstract int getTotal();
}
在你之前代码里出现的 Express express;
是一个变量声明语句,这是面向对象编程里多态性的一种应用,下面为你详细解释:
1. 变量声明
Express express;
声明了一个名为 express
的变量,该变量的类型为 Express
。这里的 Express
是一个抽象类,此抽象类定义了快递的通用属性与方法,像 weight
属性、getWeight()
方法以及抽象方法 getTotal()
。
2. 多态性的体现
多态性是面向对象编程的重要特性之一,它允许我们使用父类类型的引用去引用子类的对象。在这个场景中,Express
是父类(抽象类),SLExpress
和 DDExpress
是它的子类。
在代码里,我们依据用户输入的快递公司名称(SL
或者 DD
)来创建对应的子类对象,不过把这些子类对象赋值给父类类型的引用 express
,具体代码如下:
java
if (name.equals("SL")) {express = new SLExpress();
} else if (name.equals("DD")) {express = new DDExpress();
}
- 当
name
为"SL"
时,创建一个SLExpress
对象,并且把这个对象赋值给express
。 - 当
name
为"DD"
时,创建一个DDExpress
对象,并且把这个对象赋值给express
。
3. 好处
采用这种做法有以下好处:
- 代码的可扩展性:要是后续需要添加新的快递类型(例如新的子类),仅需创建新的子类并继承
Express
类,重写getTotal()
方法,而不用修改Main
类里大部分的代码逻辑。 - 代码的简洁性:能够使用统一的父类引用
express
来调用getTotal()
方法,不用为每个子类单独编写调用逻辑。例如:
java
express.weight = weight; // 设置快递重量
int total = express.getTotal(); // 计算运费
4. 动态绑定
在运行时,Java 会依据 express
实际引用的对象类型(SLExpress
或者 DDExpress
)来调用对应的 getTotal()
方法,这就是动态绑定。所以,尽管 express
是 Express
类型的引用,但调用 getTotal()
方法时会执行子类重写后的具体实现。
综上所述,Express express;
这种做法利用了多态性,提升了代码的可扩展性和简洁性。