基础
JDK和JRE的概述
● JDK: Java开发工具包(Java Development Kit), 包含开发工具 和 JRE.
○ 常用的开发工具: javac, java
● JRE: Java运行时环境(Java Runtime Environment), 包含运行Java程序时所需的核心类库和 JVM.
○ 核心类库: java.lang, java.util, java.io
● JVM: Java虚拟机(Java Virtual Machine)
○ 作用: 用来保证Java程序跨平台性的, 但是JVM本身并不能跨平台.
c. 目录解释:
■ bin: 存放的是编译器和工具
■ db: 存数数据
■ include: 编译本地方法.
■ jre: Java运行时文件
■ lib: 存放类库文件
■ src.zip: 存放源代码的.
idea创建项目
常用的关键字
● public: 公共的权限,
● class: 表示在创建一个类.
● static: 表示静态的意思.
● void: 表示方法的返回值类型.
//1. 定义一个Java类, 类名叫: VariableDemo01
public class VariableDemo01 {
//2. 定义main方法, 作为程序的主入口, 所有代码都是从这里开始执行的.
public static void main(String[] args) {
//3. 定义一个int类型的变量, 变量名叫a, 初始化值为: 121
int a = 121;
//4. 通过输出语句, 将变量a的值打印到控制台上.
System.out.println(a);
}
}
键盘输入
//导包
import java.util.Scanner;
public class ScannerDemo01 {
public static void main(String[] args) {
//1. 提示用户录入他/她的年龄.
System.out.println("请录入您的年龄: ");
//2. 通过键盘录入功能, 接收用户录入的年龄.
Scanner sc = new Scanner(System.in);
int age = sc.nextInt();
//3. 将用户录入的数据(年龄)打印到控制台上.
System.out.println("age: " + age);
}
}
分支
import java.util.Scanner;
public class IfDemo05 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请录入小明的考试成绩: ");
int score = sc.nextInt();
if (score >= 95 && score <= 100) {
System.out.println("奖励小明: 山地自行车一辆");
} else if(score >= 0 && score < 90) {
System.out.println("奖励小明: 男女双混组合拳 + 扫帚棒法");
} else {
System.out.println("考试成绩录入有误.");
}
}
}
case穿透
import java.util.Scanner;
public class SwitchDemo08 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请录入一个月份: ");
int month = sc.nextInt();
switch (month) {
case 12:
case 1:
case 2:
System.out.println("冬季");
break;
case 3:
case 4:
case 5:
System.out.println("春季");
break;
case 6:
case 7:
case 8:
System.out.println("夏季");
break;
case 9:
case 10:
case 11:
System.out.println("秋季");
break;
default:
System.out.println("没有这样的日期");
break;
}
}
}
循环
public class ForDemo02 {
public static void main(String[] args) {
//1. 通过for循环, 打印1~5之间的数字.
for (int i = 1; i <= 5 ; i++) {
System.out.println(i);
}
}
}
public class WhileDemo01 {
public static void main(String[] args) {
int i = 0;
while(i < 10) {
System.out.println("Hello World!");
i++;
}
}
}
Math类似Scanner,也是Java提供好的API(Application Programming Interface),内部提供了产生随机数的功能. API后续课程详细讲解,现在可以简单理解为Java已经写好的代码, 我们可以直接拿来用.
Math.random(); //可以获取0.0 ~ 1.0之间所有的数据, 包左不包右.
//示例: 获取1-100之间的随机数
int num = (int)(Math.random()*100 + 1);
数组 方法 面向对象
public class ArrayDemo01 {
public static void main(String[] args) {
//1. 创建int类型的数组, 用来存储3个元素.
//我们制定长度, 由系统给出默认值.
//格式一:
int[] arr1 = new int[3]; //推荐.
//格式二:
int arr2[] = new int[3];
//2. 创建int类型的数组, 存储数据`11, 22, 33`.
//我们直接传入元素, 由系统指定长度.
//格式1
int[] arr3 = new int[]{11, 22, 33};
//格式2
int[] arr4 = {11, 22, 33}; //推荐.
}
}
方法重载
同一个类中, 出现方法名相同, 但是参数列表不同的两个或以上的方法时称为方法重载. 方法重载与方法的返回值的数据类型无关.
public class MethodDemo01 {
public static void main(String[] args) {
//定义不同数据类型的变量
byte a = 10;
byte b = 20;
short c = 10;
short d = 20;
int e = 10;
int f = 10;
long g = 10;
long h = 20;
// 调用
System.out.println(compare(a, b));
System.out.println(compare(c, d));
System.out.println(compare(e, f));
System.out.println(compare(g, h));
}
// 两个byte类型的
public static boolean compare(byte a, byte b) {
System.out.println("byte");
return a == b;
}
// 两个short类型的
public static boolean compare(short a, short b) {
System.out.println("short");
return a == b;
}
// 两个int类型的
public static boolean compare(int a, int b) {
System.out.println("int");
return a == b;
}
// 两个long类型的
public static boolean compare(long a, long b) {
System.out.println("long");
return a == b;
}
}
//定义手机类
public class Phone {
//成员变量, 私有化, 类似于Python中的 __
private String brand; //品牌
private int price; //价格
private String color; //颜色
//构造方法, 空参, 全参, 类似于Python中的魔法方法
//空参构造
public Phone() {
}
//全参构造
public Phone(String brand, int price, String color) {
this.brand = brand;
this.price = price;
this.color = color;
}
//getXxx(), setXxx()方法, 用来获取或者设置对象的属性值的
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
//成员方法, 就是普通的函数
public void call(String name) {
System.out.println("给" + name + "打电话");
}
public void sendMessage(String name) {
System.out.println("给" + name + "发短信");
}
}
//手机类的测试类
public class PhoneTest {
public static void main(String[] args) {
//需求1: 测试空参构造.
//1. 创建手机类的对象.
Phone p1 = new Phone();
//2. 设置对象的属性值
p1.setBrand("华为P40");
p1.setPrice(3000);
p1.setColor("土豪金");
//3. 打印对象的属性值.
System.out.println(p1.getBrand() + ", " + p1.getPrice() + ", " + p1.getColor());
//4. 调用成员方法.
p1.call("夯哥");
p1.sendMessage("夯哥");
System.out.println("----------------------");
//需求2: 测试全参构造.
//1. 创建手机类的对象, 并给对象的属性赋值
Phone p2 = new Phone("Iphone12", 2000, "玫瑰红");
//2. 打印对象的属性值.
System.out.println(p2.getBrand() + ", " + p2.getPrice() + ", " + p2.getColor());
//3. 调用成员方法.
p2.call("夯哥");
p2.sendMessage("夯哥");
}
}
final关键字和static关键字
● 静态方法只能访问静态的成员变量和静态的成员方法.
● 简单记忆: 静态只能访问静态.
在Java中, 一个没有方法体的方法应该定义为抽象方法, 而类中如果有抽象方法, 该类必须定义为抽象类.
为了体现事物功能的扩展性,Java中就提供了接口来定义这些额外功能
API&异常
stringBuilder类
public class StringBuilderExample {
public static void main(String[] args) {
// 使用String进行字符串拼接
String str = "Hello";
str += " ";
str += "World";
System.out.println("Using String: " + str);
// 使用StringBuilder进行字符串拼接
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("Hello");
stringBuilder.append(" ");
stringBuilder.append("World");
System.out.println("Using StringBuilder: " + stringBuilder.toString());
}
}
自动拆装箱
public class AutoboxingUnboxingExample {
public static void main(String[] args) {
// 自动装箱
int primitiveInt = 5;
Integer wrapperInt = primitiveInt;
System.out.println("Autoboxing: " + wrapperInt);
// 自动拆箱
Integer anotherWrapperInt = new Integer(10);
int anotherPrimitiveInt = anotherWrapperInt;
System.out.println("Unboxing: " + anotherPrimitiveInt);
// 混合使用
Integer mixedWrapperInt = 20; // 自动装箱
int mixedPrimitiveInt = mixedWrapperInt + 5; // 自动拆箱和计算
System.out.println("Mixed usage: " + mixedPrimitiveInt);
}
}
反射和集合
显示命名类
interface Greeting {
void greet();
}
class EnglishGreeting implements Greeting {
@Override
public void greet() {
System.out.println("Hello!");
}
}
public class ExplicitNamedClassExample {
public static void main(String[] args) {
Greeting greeting = new EnglishGreeting();
greeting.greet(); // 输出: Hello!
}
}
匿名内部类
interface Greeting {
void greet();
}
public class AnonymousInnerClassExample {
public static void main(String[] args) {
// 使用匿名内部类来实现Greeting接口
Greeting greeting = new Greeting() {
@Override
public void greet() {
System.out.println("Hello!");
}
};
greeting.greet(); // 输出: Hello!
}
}
List是一个有序的集合,允许重复元素。常见的实现类有ArrayList和LinkedList。
import java.util.ArrayList;
import java.util.List;
public class ListExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.add("Orange");
// 打印列表中的元素
for (String fruit : list) {
System.out.println(fruit);
}
}
}
Set是一个不允许重复元素的集合。常见的实现类有HashSet、LinkedHashSet和TreeSet。
import java.util.HashSet;
import java.util.Set;
public class SetExample {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("Apple");
set.add("Banana");
set.add("Apple"); // 重复元素,不会被添加
// 打印集合中的元素
for (String fruit : set) {
System.out.println(fruit);
}
}
}
Map是一个键值对(key-value pair)集合,每个键(Key)对应一个值(Value)。常见的实现类有HashMap、LinkedHashMap和TreeMap。
import java.util.HashMap;
import java.util.Map;
public class MapExample {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("Apple", 1);
map.put("Banana", 2);
map.put("Orange", 3);
// 打印映射中的键值对
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
Queue是一种先进先出(FIFO)的数据结构。常见的实现类有LinkedList和PriorityQueue。
import java.util.LinkedList;
import java.util.Queue;
public class QueueExample {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<>();
queue.add("Apple");
queue.add("Banana");
queue.add("Orange");
// 处理队列中的元素
while (!queue.isEmpty()) {
System.out.println(queue.poll());
}
}
}
反射是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException {
//使用类的class属性来获取该类对应的Class对象
Class<Student> c1 = Student.class;
System.out.println(c1);
Class<Student> c2 = Student.class;
System.out.println(c1 == c2);
System.out.println("--------");
//调用对象的getClass()方法,返回该对象所属类对应的Class对象
Student s = new Student();
Class<? extends Student> c3 = s.getClass();
System.out.println(c1 == c3);
System.out.println("--------");
//使用Class类中的静态方法forName(String className)
Class<?> c4 = Class.forName("com.itheima_02.Student");
System.out.println(c1 == c4);
}
}
第一次System.out.println(c1);,输出c1,即输出Student类的Class对象的字符串表示,例如class com.itheima_02.Student。
第二次System.out.println(c1 == c2);,输出true,因为c1和c2是同一个Class对象。
第三次System.out.println(c1 == c3);,输出true,因为c1和c3是同一个Class对象。
第四次System.out.println(c1 == c4);,输出true,因为c1和c4是同一个Class对象。
例子 通过反射运行配置文件中指定类的指定方法
public class ReflectTest01 {
public static void main(String[] args) throws Exception {
//加载数据
Properties prop = new Properties();
FileReader fr = new FileReader("myReflect\\class.txt");
prop.load(fr);
fr.close();
/*
className=com.itheima_06.Student
methodName=study
*/
String className = prop.getProperty("className");
String methodName = prop.getProperty("methodName");
//通过反射来使用
Class<?> c = Class.forName(className);//com.itheima_06.Student
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
Method m = c.getMethod(methodName);//study
m.invoke(obj);
}
}
IO流&网络编程
Lambda
组成Lambda表达式的三要素:形式参数,箭头,代码块
例子
● 目标
○ 无参无返回值抽象方法的练习
● 需求:
○ 定义一个接口(Eatable),里面定义一个抽象方法:void eat();
○ 定义一个测试类(EatableDemo),在测试类中提供两个方法
■ 一个方法是:useEatable(Eatable e)
■ 一个方法是主方法,在主方法中调用useEatable方法
//接口
public interface Eatable {
void eat();
}
//实现类
public class EatableImpl implements Eatable {
@Override
public void eat() {
System.out.println("一天一苹果,医生远离我");
}
}
//测试类
public class EatableDemo {
public static void main(String[] args) {
//在主方法中调用useEatable方法
Eatable e = new EatableImpl();
useEatable(e);
//匿名内部类
useEatable(new Eatable() {
@Override
public void eat() {
System.out.println("一天一苹果,医生远离我");
}
});
//Lambda表达式
useEatable(() -> {
System.out.println("一天一苹果,医生远离我");
});
}
private static void useEatable(Eatable e) {
e.eat();
}
}
Lambda表达式的省略模式
public interface Addable {
int add(int x, int y);
}
public interface Flyable {
void fly(String s);
}
public class LambdaDemo {
public static void main(String[] args) {
// useAddable((int x,int y) -> {
// return x + y;
// });
//参数的类型可以省略
useAddable((x, y) -> {
return x + y;
});
// useFlyable((String s) -> {
// System.out.println(s);
// });
//如果参数有且仅有一个,那么小括号可以省略
// useFlyable(s -> {
// System.out.println(s);
// });
//如果代码块的语句只有一条,可以省略大括号和分号
useFlyable(s -> System.out.println(s));
//如果代码块的语句只有一条,可以省略大括号和分号,如果有return,return也要省略掉
useAddable((x, y) -> x + y);
}
private static void useFlyable(Flyable f) {
f.fly("风和日丽,晴空万里");
}
private static void useAddable(Addable a) {
int sum = a.add(10, 20);
System.out.println(sum);
}
Lambda表达式和匿名内部类的区别
● 所需类型不同
○ 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
○ Lambda表达式:只能是接口
● 使用限制不同
○ 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
○ 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式
● 实现原理不同
○ 匿名内部类:编译之后,产生一个单独的.class字节码文件
○ Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成
IO流
字节流一次读写一个字节数组的速度比一次读写一个字节的速度快很多(稍后测试), 这是加入了数组这样的缓冲区效果, 但是如果每次都需要我们自己来定义数组的话, 是非常繁琐的. 所以Java本身在设计的时候, 也考虑到了这样的设计思想, 并提供了字节缓冲流.
字节缓冲流也叫缓冲字节流, 高效字节流, 字节高效流,
import java.io.*;
// 通过普通字节流一次读写一个字节
public class CopyFileMethods {
public static void main(String[] args) {
// 1. 普通字节流一次读写一个字节
try (FileInputStream fis = new FileInputStream("source.mp4");
FileOutputStream fos = new FileOutputStream("destination1.mp4")) {
int byteData;
while ((byteData = fis.read()) != -1) {
fos.write(byteData);
}
} catch (IOException e) {
e.printStackTrace();
}
// 2. 普通字节流一次读写一个字节数组
try (FileInputStream fis = new FileInputStream("source.mp4");
FileOutputStream fos = new FileOutputStream("destination2.mp4")) {
byte[] buffer = new byte[1024]; // 1KB buffer
int length;
while ((length = fis.read(buffer)) != -1) {
fos.write(buffer, 0, length);
}
} catch (IOException e) {
e.printStackTrace();
}
// 3. 高效字节流一次读写一个字节
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("source.mp4"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("destination3.mp4"))) {
int byteData;
while ((byteData = bis.read()) != -1) {
bos.write(byteData);
}
} catch (IOException e) {
e.printStackTrace();
}
// 4. 高效字节流一次读写一个字节数组
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("source.mp4"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("destination4.mp4"))) {
byte[] buffer = new byte[1024]; // 1KB buffer
int length;
while ((length = bis.read(buffer)) != -1) {
bos.write(buffer, 0, length);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
对象序列化流
public class ObjectOutputStreamDemo {
public static void main(String[] args) throws IOException {
//ObjectOutputStream(OutputStream out):创建一个写入指定的OutputStream的ObjectOutputStream
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("myOtherStream\\oos.txt"));
//创建对象
Student s = new Student("林青霞",30);
//void writeObject(Object obj):将指定的对象写入ObjectOutputStream
oos.writeObject(s);
//释放资源
oos.close();
}
}
对象反序列化流
public class ObjectInputStreamDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//ObjectInputStream(InputStream in):创建从指定的InputStream读取的ObjectInputStream
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("myOtherStream\\oos.txt"));
//Object readObject():从ObjectInputStream读取一个对象
Object obj = ois.readObject();
Student s = (Student) obj;
System.out.println(s.getName() + "," + s.getAge());
ois.close();
}
}
● erialVersionUID
○ 用对象序列化流序列化了一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出问题呢?
■ 会出问题,会抛出InvalidClassException异常
○ 如果出问题了,如何解决呢?
■ 重新序列化
■ 给对象所属的类加一个serialVersionUID
● private static final long serialVersionUID = 42L;
以下是一些关于选择 serialVersionUID 的建议:
自动生成:大多数集成开发环境(IDE)如 IntelliJ IDEA 或 Eclipse 可以自动生成一个唯一的 serialVersionUID。它们通常基于类的名称、成员变量、方法等信息生成一个哈希值。
手动指定:你可以手动指定一个数值,比如 42L,但要确保它在类的生命周期内不会变更。这意味着如果你手动修改了类的结构(如增加或删除了字段),最好更新 serialVersionUID 以避免反序列化时出现 InvalidClassException。
● transient
○ 如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
■ 给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程
public class ObjectStreamDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// write();
read();
}
//反序列化
private static void read() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("myOtherStream\\oos.txt"));
Object obj = ois.readObject();
Student s = (Student) obj;
System.out.println(s.getName() + "," + s.getAge());
ois.close();
}
//序列化
private static void write() throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("myOtherStream\\oos.txt"));
Student s = new Student("林青霞", 30);
oos.writeObject(s);
oos.close();
}
}
网络编程
网络编程三要素
IP地址 端口 协议
要想让网络中的计算机能够互相通信,必须为每台计算机指定一个标识号,通过这个标识号来指定要接收数据的计算机和识别发送的计算机,而IP地址就是这个标识号。也就是设备的标识
public class InetAddressDemo {
public static void main(String[] args) throws UnknownHostException {
//InetAddress address = InetAddress.getByName("itheima");
InetAddress address = InetAddress.getByName("192.168.1.66");
//public String getHostName():获取此IP地址的主机名
String name = address.getHostName();
//public String getHostAddress():返回文本显示中的IP地址字符串
String ip = address.getHostAddress();
System.out.println("主机名:" + name);
System.out.println("IP地址:" + ip);
}
}
网络的通信,本质上是两个应用程序的通信。每台计算机都有很多的应用程序,那么在网络通信时,如何区分这些应用程序呢?如果说IP地址可以唯一标识网络中的设备,那么端口号就可以唯一标识设备中的应用程序了。也就是应用程序的标识
- 案例需求
● 客户端:数据来自于文本文件
● 服务器:接收到的数据写入文本文件 - 案例分析
a. 创建客户端,创建输入流对象指向文件,从文件循环读取数据,每读取一行就使用输出流给服务器输出一行
b. 创建服务端,创建输出流对象指向文件,从客户端接受数据,每接受一行就给文件中输出一行
public class ClientDemo {
public static void main(String[] args) throws IOException {
//创建客户端Socket对象
Socket s = new Socket("192.168.1.66",10000);
//封装文本文件的数据
BufferedReader br = new BufferedReader(new FileReader("myNet\\InetAddressDemo.java"));
//封装输出流写数据
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String line;
while ((line=br.readLine())!=null) {
bw.write(line);
bw.newLine();
bw.flush();
}
//释放资源
br.close();
s.close();
}
}
public class ServerDemo {
public static void main(String[] args) throws IOException {
//创建服务器Socket对象
ServerSocket ss = new ServerSocket(10000);
//监听客户端连接,返回一个对应的Socket对象
Socket s = ss.accept();
//接收数据
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
//把数据写入文本文件
BufferedWriter bw = new BufferedWriter(new FileWriter("myNet\\Copy.java"));
String line;
while ((line=br.readLine())!=null) {
bw.write(line);
bw.newLine();
bw.flush();
}
//释放资源
bw.close();
ss.close();
}
}
JDBC&多线程
JDBC
类----------表
类中属性----------表中字段
对象----------记录
● 常见的数据库管理系统
MYSQL:开源免费的数据库,小型的数据库.已经被Oracle收购了.MySQL6.x版本也开始收费。
Oracle:收费的大型数据库,Oracle公司的产品。Oracle收购SUN公司,收购MYSQL。
DB2 :IBM公司的数据库产品,收费的。常应用在银行系统中.
SQLServer:MicroSoft 公司收费的中型的数据库。C#、.net等语言常使用。
SyBase:已经淡出历史舞台。提供了一个非常专业数据建模的工具PowerDesigner。
SQLite:嵌入式的小型数据库,应用在手机端。
JDBC规范(掌握四个核心对象):
- DriverManager:用于注册驱动
- Connection: 表示与数据库创建的连接
- Statement: 操作数据库sql语句的对象
- ResultSet: 结果集或一张虚拟表
链接
public class JdbcUtils {
private static String driver = "com.mysql.jdbc.Driver";
private static String url = "jdbc:mysql://localhost:3306/webdb_4";
private static String user = "root";
private static String password = "root";
static{
try {
//注册驱动
Class.forName(driver);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 获得连接
* @return
* @throws SQLException
*/
public static Connection getConnection() throws SQLException{
//获得连接
Connection conn = DriverManager.getConnection(url, user, password);
return conn;
}
/**
* 释放资源
* @param conn
* @param st
* @param rs
*/
public static void closeResource(Connection conn , Statement st , ResultSet rs){
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
}
}
if(st != null){
try {
st.close();
} catch (SQLException e) {
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
}
}
}
}
增删改查
@Test
public void demo01(){
//添加
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
//1 获得连接
conn = JdbcUtils.getConnection();
//操作
//1) 获得语句执行者
st = conn.createStatement();
//2) 执行sql语句
int r = st.executeUpdate("insert into category(cname) values('测试')");
//3) 处理结果
System.out.println(r);
} catch (Exception e) {
throw new RuntimeException(e);
} finally{
//释放资源
JdbcUtils.closeResource(conn, st, rs);
}
}
@Test
public void demo02(){
//修改
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
st = conn.createStatement();
int r = st.executeUpdate("update category set cname='测试2' where cid = 4");
System.out.println(r);
} catch (Exception e) {
throw new RuntimeException(e);
} finally{
JdbcUtils.closeResource(conn, st, rs);
}
}
@Test
public void demo03(){
// 删除操作
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
st = conn.createStatement();
int r = st.executeUpdate("delete from category where cid = 4");
System.out.println(r);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
JdbcUtils.closeResource(conn, st, rs);
}
}
@Test
public void demo04(){
//通过id查询详情
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
//操作
st = conn.createStatement();
rs = st.executeQuery("select * from category where cid = 30");
if(rs.next()){
String cid = rs.getString("cid");
String cname = rs.getString("cname");
System.out.println(cid + " @ " + cname );
} else {
System.out.println("没有数据");
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally{
JdbcUtils.closeResource(conn, st, rs);
}
}
SQL注入问题
SQL注入:用户输入的内容作为了SQL语句语法的一部分,改变了原有SQL真正的意义。
假设有登录SQL语句如下:
SELECT * FROM 用户表 WHERE NAME = 用户输入的用户名 AND PASSWORD = 用户输的密码;
此时,当用户输入正确的账号与密码后,查询到了信息则让用户登录。但是当用户输入的账号为XXX 密码为:XXX’ OR ‘a’=’a时,则真正执行的代码变为:
SELECT * FROM 用户表 WHERE NAME = ‘XXX’ AND PASSWORD =’ XXX’ OR ’a’=’a’;
此时,上述查询语句时永远可以查询出结果的。那么用户就直接登录成功了,显然我们不希望看到这样的结果,这便是SQL注入问题。 为此,我们使用PreparedStatement来解决对应的问题。
基于预处理实现CURD操作
@Test
public void demo01(){
//添加:向分类表中添加数据
Connection conn = null;
PreparedStatement psmt = null;
ResultSet rs = null;
try {
//1 获得连接
conn = JdbcUtils.getConnection();
//2 处理sql语句
String sql = "insert into category(cname) values(? )";
//3获得预处理对象
psmt = conn.prepareStatement(sql);
//4设置实际参数
psmt.setString(1,"预处理");
//5执行
int r = psmt.executeUpdate();
System.out.println(r);
} catch (Exception e) {
throw new RuntimeException(e);
} finally{
//6释放资源
JdbcUtils.closeResource(conn, psmt, rs);
}
}
@Test
public void demo02(){
//修改
Connection conn = null;
PreparedStatement psmt = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
//1 sql语句
String sql = "update category set cname = ? where cid = ?";
//2 获得预处理对象
psmt = conn.prepareStatement(sql);
//3设置实际参数
psmt.setString(1, "测试数据");
psmt.setInt(2, 4);
//4执行
int r = psmt.executeUpdate();
System.out.println(r);
} catch (Exception e) {
throw new RuntimeException(e);
} finally{
JdbcUtils.closeResource(conn, psmt, rs);
}
}
@Test
public void demo05(){
//通过id查询
Connection conn = null;
PreparedStatement psmt = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
String sql = "select * from category where cid = ?";
psmt = conn.prepareStatement(sql);
psmt.setInt(1, 2);
rs = psmt.executeQuery();
if(rs.next()){
System.out.println("查询到");
} else {
System.out.println("查询不到");
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally{
JdbcUtils.closeResource(conn, psmt, rs);
}
}
连接池理解为存放多个连接的集合。使用连接池技术的目的:解决建立数据库连接耗费资源和时间很多的问题,提高性能。
Java为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商需要让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池!
常见的连接池:C3P0、DRUID。
C3P0连接池的使用
<c3p0-config>
<!-- 使用默认的配置读取连接池对象 -->
<default-config>
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day03</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 连接池参数 -->
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">10</property>
<property name="checkoutTimeout">2000</property>
<property name="maxIdleTime">1000</property>
</default-config>
</c3p0-config>
说明: c3p0 连接池常用的配置参数说明:
initialPoolSize : 初始连接数 刚创建好连接池的时候准备的连接数量
maxPoolSize : 最大连接数 连接池中最多可以放多少个连接
checkoutTimeout : 最大等待时间 连接池中没有连接时最长等待时间
maxIdleTime : 最大空闲回收时间 连接池中的空闲连接多久没有使用就会回收
public class JdbcUtils {
//创建一个C3P0的连接池对象(使用c3p0-config.xml中default-config标签中对应的参数)
public static DataSource ds = new ComboPooledDataSource();
//从池中获得一个连接
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
//释放资源
public static void closeAll(ResultSet rs, Statement stmt, Connection conn){
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
rs = null;
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
stmt = null;
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
conn = null;
}
}
}
public class Demo {
public static void main(String[] args) throws Exception {
// 拿到连接
Connection conn = JdbcUtils.getConnection();
// 执行sql语句
String sql = "INSERT INTO student VALUES (NULL, ?, ?, ?);";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "李四");
pstmt.setInt(2, 30);
pstmt.setDouble(3, 50);
int i = pstmt.executeUpdate();
System.out.println("影响的函数: " + i);
// 关闭资源
JdbcUtils.close(conn, pstmt);
}
}
事务概述
事务指的是逻辑上的一组操作,组成这组操作的各个单元要么全都成功,要么全都失败.
事务作用:保证在一个事务中多次SQL操作要么全都成功,要么全都失败. 安全性,完整性
Mysql事务操作
public class Demo01JDBC {
public static void main(String[] args) {
Connection conn = null;
Statement stat = null;
try {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取数据库连接对象Connection
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day04", "root", "root");
//开启事务
conn.setAutoCommit(false);
//3.获取执行sql语句的执行者对象Statement
stat = conn.createStatement();
//4.执行sql语句,获取结果
int row1 = stat.executeUpdate("update account set money=money-1000 where name='jack';");
System.out.println(0/0);
int row2 = stat.executeUpdate("update account set money=money+1000 where name='rose';");
//5.处理结果
if(row1>0 && row2>0){
System.out.println("转账成功!");
//提交事务
conn.commit();
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("转账失败!");
//回滚事务
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
//6.释放资源
if(stat!=null){
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
事务特征: ACID
原子性(Atomicity)原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性(Consistency)事务前后数据的完整性必须保持一致。
隔离性(Isolation)事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。
持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
并发访问问题
如果不考虑隔离性,事务存在三种并发访问问题。
● 脏读:一个事务读到了另一个事务未提交的数据.
● 不可重复读:一个事务读到了另一个事务已经提交(update)的数据。引发另一个事务,在事务中的多次查询结果不一致。
● 虚读 /幻读:一个事务读到了另一个事务已经提交(insert)的数据。导致另一个事务,在事务中多次查询的结果不一致。
多线程
线程的方式总共有两种,一种是继承Thread类方式,一种是实现Runnable接口方式
Java中通过继承Thread类来创建并启动多线程的步骤如下:
● 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。
● 创建Thread子类的实例,即创建了线程对象
● 调用线程对象的start()方法来启动该线程
public class MyThread extends Thread {
//定义指定线程名称的构造方法
public MyThread(String name) {
//调用父类的String参数的构造方法,指定线程的名称
super(name);
}
/**
* 重写run方法,完成该线程执行的逻辑
*/
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println(getName()+":正在执行!"+i);
}
}
}
采用java.lang.Runnable也是非常常见的一种,我们只需要重写run方法即可。
步骤如下:
● 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
● 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
● 调用线程对象的start()方法来启动线程。
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
public class Demo {
public static void main(String[] args) {
//创建自定义类对象 线程任务对象
MyRunnable mr = new MyRunnable();
//创建线程对象
Thread t = new Thread(mr, "小强");
t.start();
for (int i = 0; i < 20; i++) {
System.out.println("旺财 " + i);
}
}
}
匿名内部类方式
public class NoNameInnerClassThread {
public static void main(String[] args) {
// new Runnable(){
// public void run(){
// for (int i = 0; i < 20; i++) {
// System.out.println("张宇:"+i);
// }
// }
// }; //---这个整体 相当于new MyRunnable()
Runnable r = new Runnable(){
public void run(){
for (int i = 0; i < 20; i++) {
System.out.println("张宇:"+i);
}
}
};
new Thread(r).start();
for (int i = 0; i < 20; i++) {
System.out.println("费玉清:"+i);
}
}
}
有三种方式完成同步操作:
● 同步代码块。
● 同步方法。
● 锁机制。
synchronized 关键字在 Java 中用于同步代码块,确保同一时间只有一个线程可以访问该代码块或方法。这在多线程环境下尤其重要,因为多个线程可能会同时访问和修改共享资源,从而导致数据不一致的问题。
具体来说,synchronized 关键字可以应用于方法或代码块
同步方法:在方法前加上 synchronized 关键字,这样一来,调用该方法时,线程会自动获得该方法所属对象的监视器锁(monitor lock)。
public synchronized void syncMethod() {
// 需要同步的代码
}
同步代码块:在代码块前加上 synchronized 关键字,并指定一个对象作为锁,这样可以更灵活地控制同步的范围。
public void someMethod() {
synchronized(this) {
// 需要同步的代码
}
}
线程安全的类
- StringBuffer
a. 线程安全,可变的字符序列
b. 从版本JDK 5开始,被StringBuilder 替代。 通常应该使用StringBuilder类,因为它支持所有相同的操作,但它更快,因为它不执行同步 - Vector
a. 从Java 2平台v1.2开始,该类改进了List接口,使其成为Java Collections Framework的成员。 与新的集合实现不同, Vector被同步。 如果不需要线程安全的实现,建议使用ArrayList代替Vector - Hashtable
a. 该类实现了一个哈希表,它将键映射到值。 任何非null对象都可以用作键或者值
b. 从Java 2平台v1.2开始,该类进行了改进,实现了Map接口,使其成为Java Collections Framework的成员。 与新的集合实现不同, Hashtable被同步。 如果不需要线程安全的实现,建议使用HashMap代替Hashtable
线程优先级
public class ThreadPriority extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName() + ":" + i);
}
}
}
public class ThreadPriorityDemo {
public static void main(String[] args) {
ThreadPriority tp1 = new ThreadPriority();
ThreadPriority tp2 = new ThreadPriority();
ThreadPriority tp3 = new ThreadPriority();
tp1.setName("高铁");
tp2.setName("飞机");
tp3.setName("汽车");
//public final int getPriority():返回此线程的优先级
System.out.println(tp1.getPriority()); //5
System.out.println(tp2.getPriority()); //5
System.out.println(tp3.getPriority()); //5
//public final void setPriority(int newPriority):更改此线程的优先级
// tp1.setPriority(10000); //IllegalArgumentException
System.out.println(Thread.MAX_PRIORITY); //10
System.out.println(Thread.MIN_PRIORITY); //1
System.out.println(Thread.NORM_PRIORITY); //5
//设置正确的优先级
tp1.setPriority(5);
tp2.setPriority(10);
tp3.setPriority(1);
tp1.start();
tp2.start();
tp3.start();
}
}
线程控制
public class ThreadSleep extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName() + ":" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadSleepDemo {
public static void main(String[] args) {
ThreadSleep ts1 = new ThreadSleep();
ThreadSleep ts2 = new ThreadSleep();
ThreadSleep ts3 = new ThreadSleep();
ts1.setName("曹操");
ts2.setName("刘备");
ts3.setName("孙权");
ts1.start();
ts2.start();
ts3.start();
}
}
public class ThreadJoin extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName() + ":" + i);
}
}
}
public class ThreadJoinDemo {
public static void main(String[] args) {
ThreadJoin tj1 = new ThreadJoin();
ThreadJoin tj2 = new ThreadJoin();
ThreadJoin tj3 = new ThreadJoin();
tj1.setName("康熙");
tj2.setName("四阿哥");
tj3.setName("八阿哥");
tj1.start();
try {
tj1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
tj2.start();
tj3.start();
}
}
public class ThreadDaemon extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName() + ":" + i);
}
}
}
public class ThreadDaemonDemo {
public static void main(String[] args) {
ThreadDaemon td1 = new ThreadDaemon();
ThreadDaemon td2 = new ThreadDaemon();
td1.setName("关羽");
td2.setName("张飞");
//设置主线程为刘备
Thread.currentThread().setName("刘备");
//设置守护线程
td1.setDaemon(true);
td2.setDaemon(true);
td1.start();
td2.start();
for(int i=0; i<10; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
maven
总结成一段话
Maven 是一种流行的Java项目管理和构建工具,利用标准化的项目结构和构建生命周期简化项目开发。它通过 pom.xml 文件管理项目依赖,自动处理下载和版本控制,还支持强大的插件系统来完成编译、测试、打包和部署等任务。Maven 强调约定优于配置,使得项目构建过程一致且可维护。
一个简单的 Maven 项目
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>my-app</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
groupId 是 com.example,表示项目组或组织。
artifactId 是 my-app,表示项目的名称。
version 是 1.0-SNAPSHOT,表示项目的版本号。
dependencies 部分列出了项目依赖库,这里依赖了 JUnit 进行单元测试。
一小时Maven教程