我的天啊一天又要过去了,没事的还有时间!!!
不要焦虑不要焦虑,事实证明只要我认真地投入进去一切都还是来得及的,代码多实操多复盘,别叽叽喳喳胡思乱想多多思考,有迷茫前害怕后的功夫已经可以学很多东西了,不许胡思乱想了快点学,,,对,加油,相信自己哦!!
---------------------------------------------------------------------------------------------------------------------------------
这是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; 这种做法利用了多态性,提升了代码的可扩展性和简洁性。