java学习之路(自用)

前言

考研失败,面临着找工作,只好重新复习下java基础,并加强学习。


jar包使用

  1. 复制jar包到lib

  1. 添加到lib

  1. 给项目添加依赖

  1. 生成artifact部署到tomcat

IDEA(mac)快捷键

快捷键

说明

command + delete

删除当前行

command + D

复制当前行

command + ←/→

光标调到行首/行尾

option + ←/→

光标位置到单词头/尾

shift + option + ←/→

选中光标位置到单词头/尾

command + shift + ↑/↓

当前行上移/下移

control + /

添加/删除注释

option + 回车

导包

conmmand + option + L

格式化代码

control + R

运行程序

control + D

debug

fn + F8

逐行执行

fn + F7

进入代码

command + N

构造器

option + conmmand + T

Try-Catch

23/01/03(类、包、访问修饰符、封装、继承)

一个 Java 源文件中可以定义多个类,但是最多只有一个类被 public 修饰,并且这个类的类名与文件名必须相同。若这个文件中没有 public 的类,则文件名可随便命名(前提是符合规范)。要注意的是,当用 javac 指令编译有多个类的 Java 源文件时,它会给该源文件中的每一个类生成一个对应的 .class 文件。

包的本质 实际上就是创建不同的文件夹来保存类

命名规范 com.公司名.项目名.业务模块

访问修饰符

同类

同包

子类

不同包

public

protected

默认

private

封装

1.属性私有化

2.提供公共的get/set方法

继承

解决代码复用性,当多个类存在共同的属性和方法,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,因此所有的子类不需要重新定义这些属性和方法。(子类自动拥有父类定义的属性和方法)

基本语法 class 子类 extends 父类

detail

1.子类必须调用父类的构造器(super();),完成父类的初始化。

2.创建子类对象时,默认总会调用父类无参构造器,如果父类没有无参构造器,则必须用super指定使用父类的哪个构造器完成初始化。

23/01/04(多态、equals方法、房屋出租系统)

多态

方法的多态

1.重载(形参不同)

2.重写(覆盖)修饰符 返回值类型 方法名(参数列表)要完全一致。

对象的多态(前提是两个 类/对象 有继承关系)

1.对象编译类型和运行类型可以不一致。

2.编译类型在定义时就确定了,不能改变。(编译类型可以指向其子类的运行类型)

3.运行类型可以变化。

4.编译类型看定义时=号左边,运行类型看=号右边。

向上转型

父类引用指向子类的对象

可以调用父类的所有成员,不能调用子类的特有成员,最终运行效果看子类如何实现。

Animal animal = new cat();

向下转型(把父类引用强转成子类类型)

子类类型 引用名 = (子类类型)父类引用

属性没有重写(直接看编译类型,此处和方法不同)

动态绑定

方法看运行类型,属性哪里调用就用哪里

equals方法

只能判断引用类型,默认判断地址是否相等,子类往往重写该方法,用于判断内容是否相等。

23/01/05(类变量/类方法、main方法、代码块、类加载、final、抽象类、接口)

类变量/静态变量

也称为静态变量,会被该类的所有对象实例共享。

  1. 类变量随着类的加载而创建,即使没有创建对象实例也可以访问。

  1. 依然遵守访问权限。

  1. 类变量在类加载时就初始化了。

  1. 类变量随类的加载开始,随类的消亡而销毁。

类方法/静态方法

  1. 静态方法只能调用静态变量(普通方法静态和非静态变量都可以访问)

  1. 静态方法中不能使用this

  1. 一些通用的方法常背设置成静态的(如工具类),即不被创建对象实例直接使用

  1. 要访问非静态变量就需要先创建实例,再去访问

代码块/静态代码块

属于类中的成员

  1. 和方法不同,没有方法名,没有返回值,没有参数,只有方法体,只随着类加载或创建对象时隐式调用。

  1. 相当于另外一种形式的构造器,可以做到初始化的操作(如多个构造器有重复的语句可以提取到代码块中)

  1. 静态代码块,随着类的加载而执行且只执行一次,普通代码块,每创建一个对象就执行一次

类加载

1.创建对象实例时

2.创建子类对象实例,父类也会被加载

3.使用类的静态成员时(静态属性、静态方法)

代码块调用顺序

1.静态代码块和静态属性初始化

2.普通代码块和普通属性初始化

3.调用构造方法

main方法

  1. java虚拟机调用main方法,所以必须是public(因为虚拟机和main不在一个类)

  1. 因为需要不创建实例对象就执行,所以是static

final

  1. 类希望不被继承时

  1. 希望方法不被子类重写时

  1. 希望类的某个属性不被修改时

抽象类

父类的某些方法需要声明,又不确定如何实现,可以将其声明为抽象方法,该类就是抽象类。

1.抽象类不能实例化
2.抽象类不一定包含abstract方法
3.一旦包含abstract方法,则该类必须声明为abstract
4.final、static、private不能重写
5.继承抽象类的非需要实现所有的抽象方法

接口

普通类实现接口,必须将该接口的所有方法实现
抽象类实现接口可以不用实现接口的方法
接口属性只能是final的,而且是public static final

接口的多态性

1.接口引用可以指向实现了接口的类的对象

23/01/11(四种内部类、枚举)

四种内部类

1.定义在外部类的局部位置上(比如方法内)

  1. 局部内部类(有类名)

使用方法:

a.可以访问外部类的所有成员,包括私有的。
b.不想被继承可以使用final
c.如果变量和外部类重名,并在内部类中想使用外部类的变量(外部类名.this.变量名)
  1. 匿名内部类(无类名并且同时是个对象)

基本语法:

new 类/接口 (参数列表){
    类体
};

细节:

IA tiger = new IA(){
    类体
}  //1.jdk底层创建了实现IA接口的匿名内部类的对象地址返回给tiger.
   //2.匿名内部类创建一次就没有了,对象可以反复使用。
1. jdk底层创建了 实现/继承IA 接口/类/抽象类的匿名内部类的对象地址返回给tiger.
2.匿名内部类创建一次就没有了,对象可以反复使用。

2.定义在外部类的成员位置

  1. 成员内部类(无static修饰)
  1. 静态内部类(有static修饰)

枚举

特点:

1.枚举属于一种特殊的类,只包含一组有限的特定对象。
2.只读不可修改。

1.自定义实现枚举

步骤:

1.构造器私有化,防止new。
2.去掉set相关方法,防止属性被修改。
3.直接在类的内部创建固定的对象。

2.enum关键字实现枚举

enum Season02 {
SPRING("春天","温暖"),WINTER("冬天","寒冷");

    private String name;
    private String desc;

    private Season02(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }
}

特点:

1.默认继承Enum类,所以不能再继承其他类。(因为java单继承机制)
2.public static final Season SPRING = new Season("春天","温暖");

23/01/12(注解、异常、包装类、String类、集合、泛型)

注解(Annotation)

@Override 表示重写了父类的方法(从编译层面验证,如果父类没有该方法,则报错。)
@Target 修饰注解的注解,称为元注解。
@Deprecated 表示该方法已废弃。
@SuppressWarnings 编译器抑制警告。

异常处理

1.try-catch-finally 捕获异常自行处理。(finally的内容始终都要执行)

2.throws 将发生的异常抛出,交给调用者处理。

3.自定义异常

包装类

针对八种基本数据类型相应的引用类型-包装类
有了类的特点就可以调用类中的方法
八种基本数据类型:byte、short、int、long、float、double、boolean、char

1.包装类和基本数据类型的转换

//手动装箱
int n1 = 100;
//方法1
Integer integer = new Integer(n1);
//方法2
Integer integer1 = Integer.valueOf(n1); 

//手动拆箱
int i = integer.intValue();
        
//jdk5后可以自动装箱与拆箱

//自动装箱
int n2 = 100;
Integer integer2 = n2; //底层使用的Integer.valueOf(n2);

//自动拆箱
int n3 = integer2; //底层使用的是intValue();

2.包装类和String类型相互转换

//包装类 -> String
Integer i =100;
//方法1
String str1 = i + "";
//方法2
String str2 = i.toString();
//方式3
String str3 = String.valueOf(i);

//String -> 包装类
//方法1
String str4 = "12345";
Integer i2 = Integer.parseInt(str4);
//方法2
Integer i3 = new Integer(str4);

String类

1.String对象创建方式

//方式1
String s = "abc";
//方式2
String s2 = "abc";
方法1 先查看常量池中是否有"abc"的数据空间,如果有就直接指向,如果没有则创建再指向。s最终指向的是"abc"在常量池的空间地址。

方法2 现在堆中创建空间,里面维护了value属性,指向"abc"在常量池的空间,如果常量池中有"abc"的数据空间,就通过value指向,如果没有则创建再指向。value最终指向的是"abc"在常量池的空间地址。

2.String类的常见方法

3.StringBuilder类

4.StringBuffer类

Arrays类

集合

Collection接口

遍历方式

1.Iterator
2.增强for
List接口
元素有序(添加顺序与取出顺序一致)且可重复

常用方法:

//1.在index位置插入element元素
void add(int index,Object element)

//2.在index位置开始将element中的所有元素添加进来
boolean addAll(int index,Collection elements)

//3.获取index位置的元素
Object get(int index)

//4.返回obj在集合中首次出现的位置
int indexOf(Object obj)

//5.返回obj在集合中最后出现的位置
int lastIndexOf(Object obj)

//6.移除index位置的元素并返回
Object remove(int index)

//7.将index位置的元素替换为element
Object set(int index,Object element)

//8.返回从fromIndex到toIndex位置的子集合(前闭后开)
List subList(int fromIndex,int toIndex)
ArrayList类
底层 数组实现的
基本等同于Vector,但ArrayList是线程不安全的(执行效率高)
Vector类
底层也是 数组实现的
是线程同步的
LinkedList类
底层实现了 双向链表双端队列
线程不安全
ArrayList和LinkedList的选择
改查多选择ArrayList
增删多选择LinkedList
Set接口
无序没有索引,且不可重复,所以最多包含一个null
HashSet

底层其实是HashMap

老韩一直讲源码有些遭不住了,先去看泛型了

泛型

约束了元素的类型
减少了类型转换次数

23/01/13(线程、I/O流)

线程

main线程中开启thread线程
start底层会创建新的线程,run只是一个方法调用,不会启动新线程

1.创建线程的两种方式

1.继承Thread类

public class Animal{
    public static void main(String[] args) {
        Cat cat = new Cat();
        cat.start();
    }
}

class Cat extends Thread {
    @Override
    public void run() {
        super.run();
    }
}

2.实现Runable接口

public class Animal{
    public static void main(String[] args) {
        Dog dog = new Dog();
        Thread thread = new Thread(dog);
        thread.start();
    }
}

class Dog implements Runnable {
    @Override
    public void run() {
        while (true){
            System.out.println("线程启动了");
        }
    }
}

2.线程终止

设置一个loop变量,在main线程修改成false。

3.线程中断

t.interrupt();

4.线程插队

yield:线程的礼让,不一定成功
join:线程的插队,一旦插队成功就肯定先执行完插入线程的所有任务

5.用户线程和守护线程

用户线程:也叫工作线程,当线程的任务执行完以通知的方式来结束
守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
常见的守护线程:垃圾回收机制
设置守护线程: 类名.setDaemon(true);

6.线程的生命周期

7.线程的同步

Synchronized

同步:在任何同一时刻,最多只有一个线程访问,以保证数据的完整性

线程去对象那看互斥锁是否闲置,锁要求是同一个对象!

1.同步代码块
synchronized(this){
}
2.同步方法
public synchronized void m (Stirng name) {
}
3.同步静态方法
public static void m1() {
    synchronized(类名.class){
    }
}

8.互斥锁

每个对象都有一个可称为"互斥锁的标记"

9.线程死锁

10.释放锁

线程的同步方法、同步代码块执行完毕
线程的同步方法、同步代码块遇到break、return
线程的同步方法、同步代码块遇到未处理的Error或Exception
线程的同步方法、同步代码块执行了线程对象的wait()方法,当前线程暂停并释放锁

I/O流

java->文件 输出
java<-文件 输入

1.创建文件

//1.根据路径构建一个File对象
public File(String pathname)
//2.根据路径+名称构建
public File(String parent, String child)
//3.根据父目录+子路径构建
public File(File parent, String child)

方式1

public static void create01() throws IOException {
    String filePath = "/Users/Timetravel/Desktop/news1.txt";
    File file = new File(filePath);
    file.createNewFile();
}

方式2

public static void create02() throws IOException {
    File parentFile = new File("/Users/Timetravel/Desktop");
    String fileName = "news2.txt";
    File file = new File(parentFile, fileName);
    file.createNewFile();
}

抽象基类

字节流

字符流

输入流

InputStream

Reader

输出流

OutputStream

Writer

2.InputStream&OutputStream

字节
1.FileInputStream&FileOutputStream
字节输入流 作用:文件->程序
FileInputStream

1.使用单个字节的读取文件

String filePath = "/Users/Timetravel/Desktop/news1.txt";
int readData = 0;
FileInputStream fileInputStream = new FileInputStream(filePath);
//fileInputStream.read() 如果返回-1代表读取完毕
while ((readData = fileInputStream.read()) != -1) {
    System.out.print((char) readData);
}
//最后要关闭文件流,释放资源
fileInputStream.close();

2.使用read(byte[] b)读取文件

String filePath = "/Users/Timetravel/Desktop/news1.txt";
//readLen记录偏移次数
int readLen = 0;
//一次读8个字节
byte[] buf = new byte[8];
FileInputStream fileInputStream = new FileInputStream(filePath);
//fileInputStream.read() 如果返回-1代表读取完毕
while ((readLen = fileInputStream.read(buf)) != -1) {
    //读取从0开始到buf.length索引的位置的数据
    System.out.printf(new String(buf,0, readLen));
}
fileInputStream.close();
FileOutputStream

1.写入一个字节(覆盖式)

String filePath = "/Users/Timetravel/Desktop/news1.txt";
FileOutputStream fileOutputStream = new FileOutputStream(filePath);
//写入一个字节
fileOutputStream.write('A');
fileOutputStream.close();

2.写入字符串(覆盖式)

String filePath = "/Users/Timetravel/Desktop/news1.txt";
String str = "hello,world";
FileOutputStream fileOutputStream = new FileOutputStream(filePath);
//str.getBytes 字符串->数组
fileOutputStream.write(str.getBytes());
fileOutputStream.close();

3.指定读取数组的位置(覆盖式)

String filePath = "/Users/Timetravel/Desktop/news1.txt";
String str = "hello,world";
FileOutputStream fileOutputStream = new FileOutputStream(filePath);
//指定从数组中读取的起始和结束位置
fileOutputStream.write(str.getBytes(),0,str.length());
fileOutputStream.close();

4.追加式写入

String filePath = "/Users/Timetravel/Desktop/news1.txt";
//FileOutputStream(filePath,true) 为追加式
FileOutputStream fileOutputStream = new FileOutputStream(filePath,true);
//写入一个字节
fileOutputStream.write('A');
fileOutputStream.close();
2.ObjectInputStream&ObjectOutputStream
序列化=同时保存值与数据类型
序列化需要 1.Serializable(是一个标记接口) 2.Externalizable
ObjectOutputStream 提供序列化功能
ObjectInputStream 提供反序列化功能
serialVersionUID 序列化的版本号,提高版本兼容性

序列化对象时,要求里面的属性也要实现序列化接口
序列化具备可继承性,父类实现序列化,则所有子类均默认实现序列化
static和transient修饰的成员不会被序列化
ObjectOutputStream
public static void objectOutputStream01() throws IOException {
    //序列化后保存格式不是纯文本
    String filePath = "/Users/Timetravel/Desktop/news1.dat";
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
    //序列化数据保存
    //int -> Integer
    oos.write(100);
    //boolean -> Boolean
    oos.writeBoolean(true);
    //char -> Character
    oos.writeChar('a');
    //保存一个Dog对象
    oos.writeObject(new Dog("旺财",10));
    oos.close();
}
ObjectInputStream
public static void objectInputStream01() throws IOException, ClassNotFoundException {
    String filePath = "/Users/Timetravel/Desktop/news1.dat";
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
    System.out.println(ois.readInt());
    System.out.println(ois.readBoolean());
    System.out.println(ois.readChar());
    Object o = ois.readObject();
    System.out.println("运行类型=" + o.getClass());
    System.out.println("dog信息=" + o);
    ois.close();
}

3.BufferedInputStream&BufferedOutputStream
属于字节处理流
public static void bufferedInputStream01() throws IOException {
    String srcFilePath = "/Users/Timetravel/Desktop/news1.txt";
    String destFilePath =  "/Users/Timetravel/Desktop/news2.txt";
    BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFilePath));
    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFilePath));
    byte[] buff = new byte[1024];
    int readLen = 0;
    while ((readLen = bis.read(buff)) != -1) {
        bos.write(buff,0,readLen);
    }
    bis.close();
    bos.close();
}

2.Reader&Writer

字符
1.FileReader&FileWriter
字符输入流
FileReader

1.每次读取一个字符

public static void readFile01() throws IOException {
    String filePath = "/Users/Timetravel/Desktop/news1.txt";
    FileReader fileReader = new FileReader(filePath);
    int data = 0;
    //1.每次读取一个字符
    while ((data = fileReader.read()) != -1 ) {
        System.out.print((char) data);
    }
    fileReader.close();
}

2.每次读取多个字符

public static void readFile02() throws IOException {
    String filePath = "/Users/Timetravel/Desktop/news1.txt";
    FileReader fileReader = new FileReader(filePath);
    int reaLen = 0;
    char[] buf = new char[8];
    //2.每次读取8个字符
    //.read(buf)返回的是实际读取到的字符数
    while ((reaLen = fileReader.read(buf)) != -1 ) {
        System.out.print(new String(buf,0,reaLen));
    }
    fileReader.close();
}
FIleWriter
字符输出流
public static void fileWriter01() throws IOException {
    String filePath = "/Users/Timetravel/Desktop/news1.txt";
    //FileWriter(filePath);默认覆盖式写入
    FileWriter fileWriter = new FileWriter(filePath);
    char[] chars = {'a','b','c'};
    //1.写入单个字符
    fileWriter.write("C");
    //2.写入指定数组
    fileWriter.write(chars);
    //3.写入数组的指定部分
    fileWriter.write("我是个大好人!".toCharArray(),0,3);
    fileWriter.write(chars,0,3);
    //4.写入整个字符串
    fileWriter.write("你好世界");
    //5.写入字符串的指定部分
    fileWriter.write("你好",0,1);
    fileWriter.close();
}
2.BufferedReader&BufferedWriter
BufferedReade
属于字符处理流
public static void bufferedReader01() throws IOException {
    String filePath = "/Users/Timetravel/Desktop/news1.txt";
    BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));
    //按行读取
    String line;
    //当bufferedReader.readLine()返回一个null时读取完毕
    while ((line = bufferedReader.readLine()) != null) {
        System.out.println(line);
    }
    bufferedReader.close();
}
BufferedWriter
属于字符处理流
public static void BufferedWriter01() throws IOException {
    String filePath = "/Users/Timetravel/Desktop/news1.txt";
    BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath));
    bufferedWriter.write("hello,world");
    //换行
    bufferedWriter.newLine();
    bufferedWriter.close();
}
3.InputStreamReader&OutputStreamWriter
转换流
public static void inputStreamReader_() throws FileNotFoundException, UnsupportedEncodingException {
    String filePath = "/Users/Timetravel/Desktop/news1.txt";
    //1.把FileInputStream 转成 InputStreamReader 并指定编码
    InputStreamReader gbk = new InputStreamReader(new FileInputStream(filePath), "gbk");
    //2.把InputStreamReader 传入 BufferedReader字符处理流
    BufferedReader bufferedReader = new BufferedReader(gbk);
    bufferedReader.colse();
}

3.节点流&处理流

节点流:从特定的数据源读取数据
处理流:也称"包装流",对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连
处理流的优势:
1.性能的提高,增加缓冲的方式来提高I/O的效率
2.操作的便捷,处理流提供了一系列便捷的方法来一次I/O大批量的数据

TCP网络通信

  1. InetAddress

  1. InetAddressd的使用

  1. 获取本机的InetAddress对象
//1.获取本机的InetAddress对象
InetAddress host01 = InetAddress.getLocalHost();

//2.获取指定主机名的InetAddress对象
InetAddress host02 = InetAddress.getByName("MacBook-Pro");

//3.获取指定域名的InetAddress对象
InetAddress host03 = InetAddress.getByName("www.baidu.com");
  1. 通过InetAddress对象,获取相应的地址
String hostAddress = host03.getHostAddress();
  1. 通过InetAddress对象,获取相应主机名/域名
String hostName = host03.getHostName();
  1. Socket

  1. SocketTCP应用案例01

client

public class SocketTCP01Client {
    public static void main(String[] args) throws IOException {
        //1.连接本机的9999端口,并返回socket对象
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
        //2.获取和socket对象关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();
        //3.通过输出流对象,写入数据到数据通道
        outputStream.write("hello!server".getBytes());
        //4.关闭流对象和socket
        outputStream.close();
        socket.close();
        System.out.println("客户端关闭....");
    }
}

server

public class SocketTCP01Server {
    public static void main(String[] args) throws IOException {
        //1.在本机9999端口监听并等待连接,要求本机没有其他服务在占用9999端口
        ServerSocket serverSocket = new ServerSocket(9999);
        //2.serverSocket.accept()当有客户端连接的时候会返回一个Socket对象
        //如果没有客户端连接,程序会阻塞
        Socket socket = serverSocket.accept();
        //3.获取和socket对象关联的输入流对象
        InputStream inputStream = socket.getInputStream();
        //4.通过输入流对象,读取数据通道数据
        byte[] buf = new byte[1024];
        int readLen = 0;
        while ((readLen = inputStream.read(buf)) != -1) {
            System.out.println(new String(buf,0,readLen));
        }
        //5.关闭流和socket以及serverSocket
        inputStream.close();
        socket.close();
        serverSocket.close();
    }
}
  1. SocketTCP应用案例02(字节流)

client

public class SocketTCP02Client {
    public static void main(String[] args) throws IOException {
        //1.连接本机的9999端口,并返回socket对象
        Socket socket = new Socket(InetAddress.getLocalHost(), 9998);
        //2.获取和socket对象关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();
        //3.通过输出流对象,写入数据到数据通道
        //socket.shutdownOutput()发送结束标记
        outputStream.write("hello!server".getBytes());
        socket.shutdownOutput();
        //4.获取和socket对象关联的输入流对象
        InputStream inputStream = socket.getInputStream();
        //5.通过输入流对象,从数据通道读取数据
        byte[] buf = new byte[1024];
        int readLen = 0;
        while ((readLen = inputStream.read(buf)) != -1) {
            System.out.println(new String(buf,0,readLen));
        }
        //4.关闭流对象和socket
        outputStream.close();
        inputStream.close();
        socket.close();
        System.out.println("客户端关闭....");
    }
}

server

public class SocketTCP02Server {
    public static void main(String[] args) throws IOException {
        //1.在本机9999端口监听并等待连接,要求本机没有其他服务在占用9999端口
        ServerSocket serverSocket = new ServerSocket(9998);
        //2.serverSocket.accept()当有客户端连接的时候会返回一个Socket对象
        //如果没有客户端连接,程序会阻塞
        Socket socket = serverSocket.accept();
        //3.获取和socket对象关联的输入流对象
        InputStream inputStream = socket.getInputStream();
        //4.通过输入流对象,读取数据通道数据
        byte[] buf = new byte[1024];
        int readLen = 0;
        while ((readLen = inputStream.read(buf)) != -1) {
            System.out.println(new String(buf,0,readLen));
        }
        //5.获取和socket对象关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("hello,client".getBytes());
        socket.shutdownOutput();
        //6.关闭流和socket以及serverSocket
        inputStream.close();
        outputStream.close();
        socket.close();
        serverSocket.close();
        System.out.println("服务端关闭....");
    }
}

  1. SocketTCP应用案例03(字符流)

client

public class SocketTCP03Client {
    public static void main(String[] args) throws IOException {
        //1.连接本机的9999端口,并返回socket对象
        Socket socket = new Socket(InetAddress.getLocalHost(), 9998);
        //2.获取和socket对象关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();
        //3.通过输出流对象,写入数据到数据通道(字符流),使用了OutputStreamWriter转换流
        //socket.shutdownOutput()发送结束标记 字节流
        //bufferedWriter.newLine();发送结束标记 字符流,要求对方相应使用readLine()
        //如果使用字符流需手动刷新,否则数据不会写入通道
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
        bufferedWriter.write("heelo,server 字符处理流");
        bufferedWriter.newLine();
        bufferedWriter.flush();
        //4.获取和socket对象关联的输入流对象
        InputStream inputStream = socket.getInputStream();
        //5.通过输入流对象,从数据通道读取数据(字符流)
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        System.out.println(bufferedReader.readLine());
        //4.关闭流对象和socket
        bufferedReader.close();
        bufferedWriter.close();
        socket.close();
        System.out.println("客户端关闭....");
    }
}

server

public class SocketTCP03Server {
    public static void main(String[] args) throws IOException {
        //1.在本机9999端口监听并等待连接,要求本机没有其他服务在占用9999端口
        ServerSocket serverSocket = new ServerSocket(9998);
        //2.serverSocket.accept()当有客户端连接的时候会返回一个Socket对象
        //如果没有客户端连接,程序会阻塞
        Socket socket = serverSocket.accept();
        //3.获取和socket对象关联的输入流对象
        InputStream inputStream = socket.getInputStream();
        //4.通过输入流对象,读取数据通道数据(字符流)使用了OutputStreamReader转换流
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        System.out.println(bufferedReader.readLine());
        //5.获取和socket对象关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();
        //6.回复信息(字符流)
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
        bufferedWriter.write("hello client 字符流");
        bufferedWriter.newLine();
        bufferedWriter.flush();
        //7.关闭流和socket以及serverSocket
        bufferedReader.close();
        bufferedWriter.close();
        socket.close();
        serverSocket.close();
        System.out.println("服务端关闭....");
    }
}

  1. 文件上传

client

public class TCPFileUploadClient {
    public static void main(String[] args) throws Exception {
        //客户端连接服务端
        Socket socket = new Socket(InetAddress.getLocalHost(),8888);
        //创建读取磁盘文件的输入流
        String filePath = "/Users/Timetravel/Desktop/cpx01.jpg";
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));
        //bytes 就是文件对应的字节数组
        byte[] bytes = StreamUtils.streamToByteArray(bis);
        //通过Socket获取到输出流,将bytes数据发送给数据通道
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        bos.write(bytes);
        bis.close();
        socket.shutdownOutput();
        //接收服务端的消息
        InputStream inputStream = socket.getInputStream();
        String s = StreamUtils.streamToString(inputStream);
        System.out.println(s);
        inputStream.close();
        bos.close();
        socket.close();
    }
}

server

public class TCPFileUploadServer {
    public static void main(String[] args) throws Exception {
        //1.服务端在本机监听8888端口
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("服务端在8888端口监听....");
        //2.等待连接
        Socket socket = serverSocket.accept();
        //3.读取客户端发送的数据
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        byte [] bytes = StreamUtils.streamToByteArray(bis);
        //4.将得到的数据写到指定路径
        String destFilePath = "src/cpx02.jpg";
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFilePath));
        bos.write(bytes);
        bos.close();
        //5.向客户端回复"收到"
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        bufferedWriter.write("收到!");
        bufferedWriter.flush();
        socket.shutdownOutput();

        bufferedWriter.close();
        bis.close();
        socket.close();
        serverSocket.close();
    }
}

反射

  1. 获得Class类的5种方式

  1. 通过对象获得

Class c1 = person.getClass();
  1. 通过forname获得

Class c2 = Class.forName("com.cpx.upload.Student");
  1. 通过类名.class获得

Class c3 = Student.class;
  1. 基本包装类都有一个Type属性

Class c4 = Integer.TYPE;
  1. 获取父类Class类对象

Class c5 = c1.getSuperclass();
  1. 可以有Class类对象的类型

  1. class

  1. interface

  1. []

  1. eunm

  1. annotation

  1. primitiye type

  1. void

  1. 通过反射,动态创建对象

//1.获得Class类对象
Class c1 = Class.forName("com.cpx.upload.Person");
//2.1通过Class类对象创建一个对象
Person person = (Person)c1.newInstance();
//2.2通过Class类对象使用构造器创建一个对象
Constructor constructor = c1.getConstructor(String.class);
Person person1 = (Person)constructor.newInstance("张三");
System.out.println(person1.name);
//3.通过Class类对象调用普通方法
Method hi = c1.getDeclaredMethod("hi",String.class);
hi.invoke(person1,"李四");
//4.私有属性需要设置setAccessible()方法
Field name = c1.getDeclaredField("name");
name.setAccessible(true);
name.set(person1,"王五");
getMethod():获取自身能用所有的公共方法。1.类本身的public 2.继承父类的public 3.实现接口的public
getDeclaredMethod():获取类自身声明的所有方法。

JDBC

  1. 使用步骤

//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/JDBC","root","cpx19970902");
//3.创建statement
Statement statement = connection.createStatement();
//4.发送sql语句并返回结果
String sql = "select * from t_user;";
ResultSet resultSet = statement.executeQuery(sql);
//5.进行结果解析
while (resultSet.next()) {
    int id = resultSet.getInt("id");
    String account = resultSet.getString("account");
    String password = resultSet.getString("password");
    String nickname = resultSet.getString("nickname");
    System.out.println(id + "--" + account + "--" + nickname);
}
//6.关闭源
resultSet.close();
statement.close();
connection.close();
  1. PreparedStatement

 解决注入攻击及SQL语句拼接问题
注入攻击:把值写成查询关键字的一部分
Scanner scanner = new Scanner(System.in);
System.out.println("请输入账号");
String account = scanner.nextLine();
System.out.println("请输入密码");
String password = scanner.nextLine();

Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/JDBC?user=root&password=cpx19970902");

String sql = "select * from t_user where account = ? and password = ?;";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setObject(1,account);
preparedStatement.setObject(2,password);

ResultSet resultSet = preparedStatement.executeQuery();

if (resultSet.next()) {
    System.out.println("登陆成功");
} else {
    System.out.println("登陆失败");
}
  1. JDBC扩展

  1. 自增长主键回显实现

//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/JDBC?user=root&password=cpx19970902");
//3.编写SQL语句
String sql = "insert into t_user(account,password,nickname) values(?,?,?);";
//4.创建statement,并在形参传入Statement.RETURN_GENERATED_KEYS
PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
//5.占位符赋值
preparedStatement.setObject(1,"test2");
preparedStatement.setObject(2,"123456");
preparedStatement.setObject(3,"吕丹丹");
//6.发送SQL语句
int i = preparedStatement.executeUpdate();
//7.结果解析
if (i > 0) {
    System.out.println("数据插入成功");
    ResultSet generatedKeys = preparedStatement.getGeneratedKeys();
    //首先移动一行,默认为数据行的前一行,即空行
    generatedKeys.next();
    //getGeneratedKeys()返回的主键结果集只有1列
    int id = generatedKeys.getInt(1);
    System.out.println("id=" + id);
} else {
    System.out.println("数据插入失败");
}
//8.关闭资源
preparedStatement.close();
connection.close();
  1. 批量数据插入性能提升

//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/JDBC" +
        "?rewriteBatchedStatements=true","root","cpx19970902");
//3.编写SQL语句
String sql = "insert into t_user(account,password,nickname) values(?,?,?);";
//4.创建statement,并在形参传入Statement.RETURN_GENERATED_KEYS
PreparedStatement preparedStatement = connection.prepareStatement(sql);

long start = System.currentTimeMillis();

//5.占位符赋值
for (int i = 0; i < 10000; i++) {
    preparedStatement.setObject(1,"test00" + i);
    preparedStatement.setObject(2,"123456" + i);
    preparedStatement.setObject(3,"吕丹丹" + i);

    //6.不执行并追加到values后面
    preparedStatement.addBatch();
}
//7.执行批量操作
preparedStatement.executeBatch();

long end = System.currentTimeMillis();

//7.结果解析
System.out.println(end - start);
//8.关闭资源
preparedStatement.close();
connection.close();
  1. JDBC中数据库事务实现

package com.cpx.api.transaction;

import org.junit.Test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 * @Author: Cpx
 * @Date: 2023/1/19 16:18
 * @Version:
 * @Description: 银行卡业务方法,调用dao方法
 */

public class BankService {
    @Test
    public void start() throws SQLException, ClassNotFoundException {
        transfer("lvdandan","ergouzi",500);
    }

public void transfer(String addAccount,String subAccount,int money) throws SQLException, ClassNotFoundException {
    BankDao bankDao = new BankDao();
    Class.forName("com.mysql.cj.jdbc.Driver");
    Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/JDBC?user=root" + "&password=cpx19970902");
    try {
        //关闭事务自动提交
        connection.setAutoCommit(false);
        bankDao.add(addAccount,money,connection);
        System.out.println("--------");
        bankDao.sub(subAccount,money,connection);
        //事务提交
        connection.commit();
    }catch (Exception e) {
        //事务回滚
        connection.rollback();
        throw e;
    }finally {
        connection.close();
    }

    }
}
package com.cpx.api.transaction;

import java.sql.*;

/**
 * @Author: Cpx
 * @Date: 2023/1/19 13:49
 * @Version:
 * @Description:
 */

public class BankDao {

    public void add(String account,int money,Connection connection) throws ClassNotFoundException, SQLException {
        String sql = "update t_bank set money = money + ? where account = ?;";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setObject(1,money);
        preparedStatement.setObject(2,account);
        preparedStatement.executeUpdate();
        preparedStatement.close();
        System.out.println("加账成功");
    }

    public void sub(String account,int money,Connection connection) throws ClassNotFoundException, SQLException {
        String sql = "update t_bank set money = money - ? where account = ?;";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setObject(1,money);
        preparedStatement.setObject(2,account);
        preparedStatement.executeUpdate();
        preparedStatement.close();
        System.out.println("减钱成功");
    }

}
  1. Druid连接池技术

  1. 硬编码方式
//1.创建一个druid连接池对象
DruidDataSource druidDataSource = new DruidDataSource();
//2.设置连接池参数 
//  必须:url,user,password
//非必须:初始化连接数,最大连接数
druidDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/JDBC");
druidDataSource.setUsername("root");
druidDataSource.setPassword("cpx19970902");
druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
//3.获取连接(通用方法)
DruidPooledConnection connection = druidDataSource.getConnection();
//4.回收连接(通用方法):用完后放回到连接池中
connection.close();
  1. 软编码方式
//1.读取外部配置文件
Properties properties = new Properties();
InputStream resourceAsStream = DruidUsePart.class.getClassLoader().getResourceAsStream("druid.properties");
properties.load(resourceAsStream);
//2.使用连接池的工具类的工厂模式创建连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
Connection connection = dataSource.getConnection();

connection.close();

配置文件

#druid连接池需要的配置参数,key固定命名
driverClassname=com.mysql.cj.jdbc.Driver
username=root
password=cpx19970902
url=jdbc:mysql://127.0.0.1:3306/JDBC
  1. 工具类封装
package com.cpx.api.utils;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

/**
 * @Author: Cpx
 * @Date: 2023/1/19 17:30
 * @Version:
 * @Description: 内部包含一个连接池对象,并且对外提供获取连接和回收连接的方法
 */

public class JdbcUtils {
    private static DataSource dataSource = null;
    private static ThreadLocal tl = new ThreadLocal<>();
    static {
        Properties properties = new Properties();
        InputStream resourceAsStream = JdbcUtils.class.getClassLoader().getResourceAsStream("druid.properties");
        try {
            properties.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() throws SQLException {
        //先检查本地变量中是否存在
        Connection connection = tl.get();
        //如果第一次没有
        if (connection == null) {
            //通过连接池获取
            connection = dataSource.getConnection();
            tl.set(connection);
        }
        return connection;
    }

    public static void freeConnection() throws SQLException {
        Connection connection = tl.get();
        if (connection != null) {
            //清空线程本地数据
            tl.remove();
            //事务状态回归
            connection.setAutoCommit(true);
            //回收到连接池
            connection.close();
        }
    }
}

ThreadLocal 可以为同一个线程存储共享变量

JavaWeb

  1. Servlet

Servlet是JavaEE规范之一 规范就是接口
Servlet是JavaWeb三大组件之一.分别是Servlet程序、filter过滤器、Listener监听器
Servlet是运行在服务器上的一个Java小程序,可以接收客户端发送的请求并响应数据给客户端
  1. Servlet实现

  1. 配置xml


    
        
        HelloServlet
        
        com.cpx.servlet.HelloServlet
    

    
    
        
        HelloServlet
        
        
        /hello
    
  1. Servlet生命周期

执行Servlet构造器 第一次访问创建Servlet时调用
执行init初始化方法 第一次访问创建时Servlet时调用
执行service方法 每次访问都会调用
执行destroy销毁方法 web工程停止的时候调用
  1. servlet构造器方法
  1. init初始化方法
  1. service方法
  1. destroy方法
  1. Maven

  1. Mavend坐标

goupId:定义当前Maven项目隶属组织
artifactId:定义当前Maven项目名 (通常是模块名称)
version:版本号
  1. MyBatis

MyBatis是持久层框架,用于简化JDBC开发(持久层:数据保存到数据库的那一层代码)

Spring5

你可能感兴趣的:(java,学习,开发语言)