Java后端学习计划(Java SE篇)Day 1
一日总结:今天学习了基础语法、异常处理以及多线程编辑的一半。基础语法分为数据、结构和对象,有这些可以构成一个基础的框架。异常处理中检查时的异常和编译时的异常的区别就在于一个在编译开始前报错一个在编译过程中报错。多线程编程,一个进程由多个线程组成,一个程序可以有多个进程,线程的创建有三种方法,根据任务的类型选择,如果是不需要重复的任务直接用继承Thread方法重写run方法,如果是要共享任务资源的可以选用实现Runable接口,如果需要返回几个进行异步处理的话选择Callable+Future。
感觉知识点比较好理解,但是运用起来比较困难,不知从何下手,现在时不时会试着在脑中构建起框架,可还是好难记住啊,希望实战过程中记住吧。
一、基础语法
变量与数据类型
1.基本数据类型有哪些?
- 整型:byte,short,int,long
- 浮点型:float,double
- 字符型:char
- 布尔型:boolean
2.引用数据类型有哪些?
- 类(Class)类型
- 接口(interface)类型
- 数组(array)类型
- 枚举类型
- 注解类型
- 集合框架类型
3.引用数据类型与基础数据类型的区别是什么?
- 存储内容:前存地址,后存值
- 默认值:前为null,后为0
- 内存分配:前为堆内存(Heap),后为栈内存(Stack)
堆内存与栈内存的区别是什么?堆与栈总结(持续更新ing)
- 运算方式:前通过方法操作对象,后支持算法与逻辑
运算符
1.运算符有哪些?
- 算术运算符:加减乘除,取余%,自增++自减--
- 关系运算符:==,!=,>,<,<=,>=
- 逻辑运算符:与&& 或|| 非!
- 位运算符:(按位)与& 或| 异或^ 取反~ 左移<< 右移>> 无符号右移>>>
- 赋值运算符:= 可搭配算术运算符和位运算符使用
- 三元运算符: 条件 ?A:B 条件为真则返回A,反则B
2.运算符的优先级(从高到低)?
()
括号++
、--
自增、自减+
、-
一元正负号*
、/
、%
乘、除、取余+
、-
加、减<<
、>>
、>>>
位移<
、<=
、>
、>=
比较==
、!=
等于、不等于&
按位与^
按位异或|
按位或&&
逻辑与||
逻辑或?:
三元=
、+=
、-=
等赋值
tip:此处引用豆包排列的结果
控制结构
1.条件语句有哪些?
if-else
int age = 18;
if (age >= 18) {
System.out.println("已经成年");
} else {
System.out.println("还未成年");
}
switch
int day = 3;
switch (day) {
case 1:
System.out.println("星期一");
break;
case 2:
System.out.println("星期二");
break;
case 3:
System.out.println("星期三");
break;
default:
System.out.println("其他");
}
2.循环语句有哪些?
for
for (int i = 0; i < 5; i++) {
System.out.println(i);
}
while
int i = 0;
while (i < 5) {
System.out.println(i);
i++;
}
do-while
int i = 0;
do {
System.out.println(i);
i++;
} while (i < 5);
Q:while和do-while有什么区别?
A:do-while先执行一遍循环体再判断条件,而while是先判断条件,当条件为真时在执行一次循环体
面向对象
1.类与对象的关系?
类为对象提供模板,对象是类的实例
2.类的特性?
封装:将属性和方法存储在一个类中并且隐藏细节,通过访问控制符进行访问,提高代码安全性和可维护性
访问控制修饰符:
public
:所有类可见。protected
:同一包内的类及不同包的子类可见。default
(无修饰符):同一包内的类可见。private
:仅本类可见。
继承:extend,Java只支持单继承,提高代码复用性
多态:根据类与类之间的关系实现重写不同的方法
3.抽象类与接口?
抽象类是包含抽象方法的类,不能实例化,只能被继承
接口是包含抽象方法,一个类可以实现implements多个接口
4.构造方法?
创建时自动调用的方法,用于初始化对象
class Person {
String name;
int age;
// 构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
// 创建对象时调用构造方法
Person person = new Person("Bob", 25);
方法名与类名相同,没有返回类型(void)
5.静态成员
静态变量:属于类,所有对象共享同一个副本。
静态方法:不依赖于对象,可以直接通过类名调用。
class MathUtils {
static final double PI = 3.14159; // 静态常量
static int add(int a, int b) { // 静态方法
return a + b;
}
}
// 使用
double pi = MathUtils.PI;
int sum = MathUtils.add(3, 5);
二、异常处理
异常分类
1.异常分为哪几类呀?
tip:这里豆包比我总结的清晰就直接引用了
Throwable
├── Error (系统错误,不可恢复)
│ ├── OutOfMemoryError
│ ├── StackOverflowError
│ └── ...
└── Exception (程序可处理的异常)
├── RuntimeException (Unchecked异常,运行时异常)
│ ├── NullPointerException
│ ├── ArrayIndexOutOfBoundsException
│ ├── ArithmeticException
│ └── ...
└── Checked异常 (编译时强制检查)
├── IOException
├── SQLException
├── ParseException
└── ...
除此之外还有自定义异常
2.Check异常和Runtime异常的区别
Check异常我称它为“检查时发现的错误”,顾名思义就是在还没开始编译之前就出现的错误提示,比如空文件啊,主要是缺少一些必要的东西。
Runtime异常我称它为“运行时发现的错误”,也很好理解,比如发现数组越界啦,在编译到某个地方时才会报错。
异常处理机制
异常的处理机制有try catch finnally 和 throw throws,
前者结构为
try{
//此处放你要检查的代码
}catch(Exception e){
//此处放处理特定异常类型的代码
}
finally{
//无论是否发生异常此处代码都会执行,即使 try 或 catch 块中存在 return 语句。
//如果 finally 块中也有 return 语句,会覆盖 try 或 catch 块中的 return 结果。
}
后者结构为
public void readFile() throws IOException {
// 在readFile方法中的代码可能抛出IOException
//throws于方法签名中使用,为声明异常
//一个方法可以声明多个异常,但不一定存在异常
}
public void validateAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("年龄不能为负数");
//throw在方法内部,为抛出异常
//通常跟着对象的实例,一定会抛出一个异常
}
}
自定义异常
实际上是一个继承Exception的类,可以针对其中的异常方法进行重写,有助于我们直接获得异常的信息(省去翻译狗头)
// 自定义Checked异常(需要强制处理)
public class UserValidationException extends Exception {
// 构造方法(至少保留以下几种)
public UserValidationException() {
super(); // 调用父类无参构造
}
public UserValidationException(String message) {
super(message); // 传递错误信息
}
public UserValidationException(String message, Throwable cause) {
super(message, cause); // 传递错误信息和原始异常
}
}
// 业务异常基类(Unchecked)
public abstract class BusinessException extends RuntimeException {
public BusinessException(String message) {
super(message);
}
}
// 具体业务异常
public class UserNotFoundException extends BusinessException {
public UserNotFoundException(String message) {
super(message);
}
}
// 自定义Unchecked异常(无需强制处理)
public class InvalidInputException extends RuntimeException {
public InvalidInputException() {
super();
}
public InvalidInputException(String message) {
super(message);
}
public InvalidInputException(String message, Throwable cause) {
super(message, cause);
}
}
三、多线程编辑
线程的创建与启动
1.什么是线程?
在了解线程之前我们得了解线程为谁服务——进程,进程是程序的一次执行实例,就比如我们的浏览器app就相当于一个程序,而我们使用浏览器app时打开的网页就相当于一个进程,那么网页上出现的文字页面效果,文本框,你输入的账号等等工作有谁来响应展示呢?没错就是一大批的线程。
简而言之,线程是进程内的一个执行单元,也是程序执行流中的最小单元,一个进程可以包含多个线程,它们并发执行,共同完成进程的任务。
2.如何创建和启动一个线程
一共有三种核心的方式
第一种:继承 thread类 (每日一个英语单词thread n.线,线索,思路)让我们直接看例子
public class MyThread extends Thread {
@Override
public void run() {
// 线程执行的代码逻辑
System.out.println("线程 " + Thread.currentThread().getName() + " 正在运行");
try {
Thread.sleep(1000); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程 " + Thread.currentThread().getName() + " 执行完毕");
}
public static void main(String[] args) {
// 创建并启动线程
Thread thread1 = new MyThread();
Thread thread2 = new MyThread();
thread1.setName("线程-A");
thread2.setName("线程-B");
thread1.start(); // 启动线程(会自动调用 run() 方法)
thread2.start();
// 注意:直接调用 run() 方法不会创建新线程!
// thread1.run(); // 这只是普通的方法调用,会在主线程执行
}
}
MyThread类继承Thread方法,并重写其run方法,run方法的作用就是线程执行的内容,我们可以简称它为要执行的任务,默认情况下如果一个线程只有一个实例的话,他就会自动使用主线程“执行任务”,若拥有两个实例的话,则需要新建一个线程,分别在不同的线程执行个性化的任务。
此处通过start方法进行启动线程,简单方便,不过由于类的单继承性,无法扩充其他的内容
那么有没有什么接口也可以实现创建和启动线程呢?有的兄弟,有的
第二种:实现Runable方法,321上代码
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("线程 " + Thread.currentThread().getName() + " 正在运行");
//获取当前运行的线程名称
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程 " + Thread.currentThread().getName() + " 执行完毕");
}
public static void main(String[] args) {
// 创建 Runnable 实例
Runnable task = new MyRunnable();
// 将 Runnable 传递给 Thread 类
Thread thread1 = new Thread(task, "线程-A");
Thread thread2 = new Thread(task, "线程-B");
thread1.start();
thread2.start();
}
}
MyRunable类实现Runable接口,并且重写其 run方法,他对比其Thread类优势在于避免单继承的限制,适合多个线程共享一个任务,不过无法直接返回执行结果(需通过共享变量或其他方式获取)。
主播主播?有没有版本最优解呢?有的兄弟,有的
第三种:实现Callable接口+Future
通过实现
Callable
接口(支持返回值),结合FutureTask
或线程池获取结果。
import java.util.concurrent.*;
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("线程 " + Thread.currentThread().getName() + " 正在计算");
Thread.sleep(1000);
return 1 + 2; // 返回计算结果
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 方式一:使用 FutureTask + Thread
Callable<Integer> task = new MyCallable();
FutureTask<Integer> futureTask = new FutureTask<>(task);
Thread thread = new Thread(futureTask, "计算线程");
thread.start();
// 获取结果(阻塞直到任务完成)
Integer result = futureTask.get();
System.out.println("计算结果:" + result);
// 方式二:使用线程池(推荐)
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(task);
Integer result2 = future.get();
System.out.println("线程池结果:" + result2);
executor.shutdown();
}
}
支持返回值,可以抛出异常,适合需要异步计算结果的场景 (我称它为“分工合作”)
典型应用场景
- Web 服务器处理请求
- 示例:用户上传大文件时,服务器立即返回 “处理中” 响应,异步进行文件存储和处理。
- 微服务远程调用
- 示例:服务 A 调用服务 B、C 获取数据,可并行发起请求,无需等待单个服务返回。
- 批量数据处理
- 示例:并行计算多个任务(如数据分析、图像处理),汇总结果。
- 定时 / 延迟任务
- 示例:定时生成报表、延迟执行异步通知。
线程状态与生命周期
tip:这里又要借用到豆包的逻辑图了
start()
┌───┐
▼ │
NEW ───── RUNNABLE ───────┐
│ │ │
│ ▼ │
│ BLOCKED │
│ │ │
│ ▼ │
│ WAITING ─────┘
│ │
│ ▼
│ TIMED_WAITING ─┐
│ │ │
└──► TERMINATED ◄┘