Java入门
1、IDEA常用快捷键
2、编译、运行代码
3、JDK组成、跨平台原理
4、关键字
Java语法
1、基本数据类型
2、类型转换
2.1、自动类型转换
2.2、表达式的自动类型转换
2.3、强制类型转换
常用API
1、String
1.1、 String对象的创建
1.2、String对象常用API
1.3、String的注意事项
2、时间API
2.1、传统API:Date
import java.util.Date;
public class DataDemo {
public static void main(String[] args) {
//1、系统时间信息
Date date = new Date();
System.out.printf(date.toString());
//2、获取时间毫秒值
long time = date.getTime();
System.out.println(time);
//3、时间戳转日期(再加2s)
time += 2*1000;
Date date1 = new Date(time);
System.out.println(date1);
//4、日期转时间戳
Date date2 = new Date();
date2.setTime(time);
System.out.println(date2);
}
}
2.2、SimpleDateFormat 时间格式
public class SimpleDateFormatDemo {
public static void main(String[] args) throws ParseException {
Date date = new Date();
System.out.println(date);
long time = date.getTime();
System.out.println(time);
//格式化日期对象、时间、毫秒值
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss EEE a");
String format1 = sdf.format(date);
System.out.println(format1);
String format2 = sdf.format(time );
System.out.println(format2);
//SimpleDateFormat解析字符串时间成为日期对象。
String dateStr = "2022-12-12 12:12:11";
//1、创建简单日期格式化对象,指定的时间格式必须与被解析的时间格式一模一样,否则程序会出bug.
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d2 = sdf2.parse(dateStr);
System.out.println(d2);
}
}
2.3、JDK8新增时间
时间戳
格式化器
集合框架
1、集合体系结构
2、Collection集合
2.1、常用方法:
2.2、迭代器:
/**
* 迭代器
*/
public class CollectionTest01 {
public static void main(String[] args) {
Collection<String> c = new ArrayList<>();c.add("赵敏");
c.add("小昭");
c.add("素素");
c.add("灭绝");
System.out.println(c);
// 使用迭代器遍历集合
Iterator<String> it = c.iterator();
// it.hasNext()即迭代器是否有下一个元素
while (it.hasNext()) {
// it.next()即迭代器获取下一个元素,初始是第一个元素
String s = it.next();
System.out.println(s);
}
}
}
2.3、增强for循环
2.4、Lambda表达式遍历集合
3、List集合
3.1、List集合特有方法
3.2、ArrayList集合底层原来
3.3、LinkedList集合底层原理
LinkedList是基于双链表实现的
比如银行排队问题,就是典型的队列问题
4、Set集合
4.1、特点
4.2、HashSet集合底层原理
如果数组快占满了,就会进行2倍扩容,即当数据中的元素达到16*0.75=12时就会扩容
如果链表过长,导致查询性能下降,怎么办呢?在JDK8之后引入红黑树解决
有个问题:那么如果对于内容相同的不同对象,比如两个信息一样的学生对象,按理应该去重,但是在HashSet集合中是使用equals()方法来比较的,即比较地址,那么就会两个对象都存入集合中,如果想要内容相同的对象也进行去重,就要重写equals()方法:Alt+Ins选择重写equals和hashCode,那么IDEA就会自动生成重写后的equals和hashCode方法
4.3、树
那么问题来了,什么是红黑树呢?
红黑树底层有自己的自平衡的算法
4.4、LinkedHashSet集合底层原理
和HashSet底层一样,只是引入了双链表机制,即每一个元素都记录了上一个和下一个元素的地址,从而实现元素有序,但是这样会更耗费内存
4.5、TreeSet集合
红黑树的左大右小的数据结构,从左往右遍历本身就可以实现升序排序
/**
* 方式一
*/
@Data
public class Student implements Comparable<Student> {
private String name;
private Integer age;
// 自定义比较规则
@Override
public int compareTo(Student o) {
//如果认为左边对象大于右边对象返回正整数
//如果认为左边对象小于右边对象返回负整数
// 如果认为左边对象等于右边对象返回0
// 需求:按照年龄升序排序
return this.age - o.age;
}
}
public class SetTest {
public static void main(String[] args) {
// 方式二
Set<Student> s = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
//如果认为左边对象大于右边对象返回正整数
//如果认为左边对象小于右边对象返回负整数
// 如果认为左边对象等于右边对象返回0
// 需求:按照年龄升序排序
return o1.getAge() - o2.getAge();
}
});
s.add(new Student("张三",10));
s.add(new Student("李四",30));
s.add(new Student("王五",20));
System.out.println(s);
}
}
5、集合的并发修改异常问题
因为当 list.remove() 删除元素之后,元素的个数就会减少,往后的元素就会向左移动,对于的下标也会减一
6、Map集合
6.1、Map集合公有
6.2、HashMap集合底层原理
HashMap将一个键值对当成一个Entry对象,其他的和HashSet集合一样的原理
6.3、LinkedHashMap集合底层原理
6.4、TreeMap集合底层原理
Java8 新特性
1、接口上
新增了默认方法default、私有方法private、静态方法static
public class InterfaceTest {
public static void main(String[] args) {
MyClass myClass = new MyClass();
myClass.defaultMethod();
MyInterface.staticMethod();
}
public interface MyInterface {
default void defaultMethod() {
System.out.println("接口中的默认方法,意味着实现类不用重写也能调用");
privateMethod();
}
private void privateMethod() {
System.out.println("这接口这的私有方法,JDK9之后才支持");
}
static void staticMethod(){
System.out.println("接口中的静态方法");
}
}
public static class MyClass implements MyInterface {}
}
2、Lambda表达式
2.1、Lambda之常规写法(只能对接口简化)
public class LambdaTest01 {
public static void main(String[] args) {
// 原先写法(创建这个接口的匿名内部类)
A a = new A() {
@Override
public void test1() {
System.out.println("原先写法");
System.out.println("test1");
}
};
a.test1();
// Lambda写法
A a1 = () -> {
System.out.println("原先写法");
System.out.println("test1");
};
a1.test1();
}
}
interface A{
void test1();
}
2.2 、Lambda之静态方法的引用
public class LambdaTest02 {
public static void main(String[] args) {
Student[] student = new Student[3];
student[0] = new Student(18, "张三");
student[1] = new Student(19, "李四");
student[2] = new Student(20, "王五");
// 原先写法 sort中第一个参数是要排序的数组,第二个参数是定义排序规则的比较器(Comparator)
Arrays.sort(student, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
//这个方法返回一个整数,该整数用于表示o1和o2的顺序。
// 如果返回值小于0,则o1排在o2之前;如果返回值等于0,
// 则o1和o2的顺序无关紧要;如果返回值大于0,则o1排在o2之后
return o1.getAge() - o2.getAge(); // 按照年龄排序()
}
});
// 现在使用Lambda表达式
Arrays.sort(student, (o1, o2) -> o1.getAge() - o2.getAge());
// 静态方法的引用,进一步简化Lambda表达式 ******************************
A a = new A();
Arrays.sort(student, a::myCompare);
System.out.println(Arrays.toString(student));
}
static class A{
public int myCompare(Student o1, Student o2) {
return o1.getAge() - o2.getAge(); // 按照年龄排序()
}
}
@Data // 自动生成getters和setters和toString方法
@AllArgsConstructor // 自动生成全参构造方法
public static class Student{
private int age;
private String name;
}
}
2.3、Lambda之特定方法的引用
public class LambdaTest03 {
public static void main(String[] args) {
B b1 = new B();
// 常规Lambda
A a1 = (B b, Integer s) -> b.method(s);
// 特定方法的引用的Lambda
A a2 = B::method;
a1.test(b1, 1);
a2.test(b1, 3);
}
interface A{
void test(B b, Integer i);
}
static class B{
void method(Integer i){
System.out.println(i);
}
}
}
2.4、Lambda之构造器引用
public class LambdaTest04 {
public static void main(String[] args) {
A a1 = new A() {
@Override
public void testB(String name, Integer age) {
new B(name, age);
}
};
// 常规Lambda写法
A a2 = (name, age) -> new B(name, age);
a2.testB("小红", 18);
// Lambda写法之构造器引用
A a3 = B::new;
a3.testB("小张", 18);
}
interface A{
void testB(String name, Integer age);
}
@Data // getter、setter、toString方法
@AllArgsConstructor //全参构造函数
static class B{
private String name;
private Integer age;
}
}
3、Stream流
3.1入门案例
public class StreamTest01 {
// 入门案例
public static void main(String[] args) {
// 取出名字以张开头的并且3个字的名字
List<String> names = new ArrayList<>();
// 为names集合添加多个元素
Collections.addAll(names,"张三丰", "张无忌", "周芷若", "赵敏", "张强");
System.out.println(names);
// names =[张三丰,张无忌,周芷若,赵敏,张强]
// 找出姓张,且是3个字的名字,存入到一个新集合中去
List<String> list = new ArrayList<>();
for (String name : names) {
//startsWith是字符串的一个方法
if (name.startsWith("张") && name.length() == 3) {
list.add(name);
}
}
System.out.println(list);
// 使用Stream流来处理(lambda 表达式)
List<String> list2 = names.stream().filter(s -> s.startsWith("张"))
.filter(a -> a.length()==3).collect(Collectors.toList());
System.out.println(list2);
}
}
3.2、获取Stream流
/**
* 获取Stream流
*/
public class GetStream {
public static void main(String[] args) {
// 1、获取List集合的Stream流
List<String> names = new ArrayList<>();
Collections.addAll(names,"张三丰", "张无忌", "周芷若", "赵敏", "张强");
// 2、获取List集合的Stream流
Stream<String> stream = names.stream();
// 2、获取Set集合的Stream流
Set<String> set = new HashSet<>();
Collections.addAll(set, "刘德华", "张曼玉", "蜘蛛精", "马德","德玛西亚");
Stream<String> stream1 = set.stream();
// 获取Set集合Stream流
// filter过滤方法,s是集合遍历的元素,contains是字符串中的方法,表示字符串中含有“德”
// forEach方法,s是过滤之后的集合遍历的元素,表示打印集合中的元素
stream1.filter(s -> s.contains("德")).forEach(s -> System.out .println(s));
// 3、获取Map集合的Stream流
Map<String,Double> map = new HashMap<>();
map.put("古力娜扎", 172.3);
map.put("迪丽热巴", 168.3);
map.put("马尔扎哈", 166.3);
// 将map集合中的key取出来存入Set集合
Set<String> keys = map.keySet();
// 获取stream流
Stream<String> ks = keys.stream();
// 将map集合中的key取出来存入Set集合
Collection<Double> values = map.values();
// 获取stream流方式一
Stream<Double> vs = values.stream();
// 也可以用Map.Entry<>方法()存入Set集合
// entries打印的值为:[迪丽热巴=168.3, 古力娜扎=172.3, 马尔扎哈=166.3]
Set<Map.Entry<String, Double>> entries = map.entrySet() ;
// 获取stream流方式二
Stream<Map.Entry<String, Double>> kvs = entries.stream();
// e表示Set集合中遍历的元素,e.getKey().contains("扎")判断元素的key是否包含“扎”
kvs.filter(e -> e.getKey().contains("扎"))
//forEach表示遍历过滤后的集合,e.getValue()获取元素的value
.forEach(e -> System.out.println(e.getKey()+ "-->" + e.getValue()));
// 4、如何获取数组的Stream流?
String[] names2 = {"张翠山", "东方不败", "唐大山", "独孤求败"};
// 获取stream流方式一
Stream<String> s1 = Arrays.stream(names2);
// 获取stream流方式二
Stream<String> s2 = Stream.of(names2);
}
}
3.3、常见的Stream流中间方法与终结方法
多线程
1、概述
2、多线程的创建
2.1、extends Thread 创建
/**
* extends Thread 线程的创建
*/
public class ThreadTest01 {
// main是一条主线程
public static void main(String[] args) {
MyThread myThread = new MyThread();
// 启动线程,开启另一个线程
myThread.start();
for (int i = 0; i < 5; i++) {
System.out.println("主线程输出:" + i);
}
}
// 方式一创建一个子线程
public static class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("子线程输出:" + i);
}
}
}
}
2.2、implements Runnable 创建
/**
* implements Runnable 线程的创建
*/
public class ThreadTest02 {
// main是一条主线程
public static void main(String[] args) {
// 创建任务对象
Runnable thread = new MyThread();
// 把任务对象交给线程对象去执行
new Thread(thread).start();
for (int i = 0; i < 5; i++) {
System.out.println("主线程输出:" + i);
}
}
// 方式二创建一个子线程
public static class MyThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("子线程输出:" + i);
}
}
}
}
最后还可以使用Lambda表达式继续简化一下代码
2.3、implements Callable 创建
/**
* implements Callable 线程的创建
*/
public class ThreadTest03 {
// main是一条主线程
public static void main(String[] args) throws Exception{
// 创建Callable对象
Callable<String> callable = new MyThread(100);
// 把Callable对象封装成FutureTask对象(未来任务对象)
// 其实FutureTask也实现了Runnable接口
FutureTask<String> futureTask = new FutureTask<>(callable);
new Thread(futureTask).start();
//获取线程执行完毕后返回的结果
//注意:如果执行到这儿,假如上面的线程还没有执行完毕
//这里的代码会暂停,等待上面线程执行完毕后才会获取结果
String s = futureTask.get();
System.out.println(s);
}
// 方式三创建一个子线程,Callable<String>中的String是call方法的返回值
public static class MyThread implements Callable<String> {
private int n;
public MyThread(int n) {
this.n = n;
}
@Override
public String call() {
int sum = 0;
for (int i = 1; i < n; i++) {
sum = sum + i;
}
return "线程求出了:1+2+3+...+"+n+"=" + sum;
}
}
}
3、线程安全问题
/**
* 线程安全案例
*/
public class ThreadSecurityTest {
public static void main(String[] args) {
// 1.创建一个账户对象
Account account = new Account("ICBC-110", 10000);
// 2.创建两个线程,去同一个账户取钱
new DrawThread(account, 10000, "小红").start();
new DrawThread(account, 10000, "小明").start();
}
// 账户类
@Data
@AllArgsConstructor
public static class Account {
private String cardId; // 卡号
private double money; // 余额
// 取钱逻辑
public void drawMoney(double getMoney) {
// 获取当前线程名字
String name = Thread.currentThread().getName();
if (money >= getMoney) {
System.out.println(name + "取钱" +getMoney + "元成功");
money -= getMoney;
System.out.println("余额为:" + money);
} else {
System.out.println(name + "取钱:余额不足,余额为:" + money);
}
}
}
// 线程取钱类
@Data
public static class DrawThread extends Thread {
private Account account;
private double money; // 取钱金额
public DrawThread(Account account, double money, String name) {
// 设置线程名称
super(name);
this.account = account;
this.money = money;
}
@Override
public void run() {
// 调用账户取钱方法
account.drawMoney(money);
}
}
}
4、解决线程安全---线程同步
4.1、同步代码块
使用synchronized关键字加锁
// 使用同步锁"@@小飞"相当于锁的一个唯一标识
synchronized ("@@小飞") {
if (money >= getMoney) {
System.out.println(name + "取钱" +getMoney + "元成功");
money -= getMoney;
System.out.println("余额为:" + money);
} else {
System.out.println(name + "取钱:余额不足,余额为:" + money);
}
}
4.2、同步方法
锁范围小意味着性能好
4.3、Lock锁
加锁代码使用try-catch-finally代码块,防止程序异常导致无法解锁,创建Lock锁使用final关键字,防止被修改
4.4、乐观锁与悲观锁
无论是使用synchronized关键字还是使用Lock锁其实本质上都是基于悲观锁的。都是先上锁,线程无法同时执行。如果想线程安全又要同时执行,就可以使用乐观锁。
悲观锁:一上来就加锁,没有安全感。每次只能一个线程进入访问完毕后,再解锁。线程安全,性能较差!
乐观锁:一开始不上锁,认为是没有问题的,大家一起跑,等要出现线程安全问题的时候才开始控制。要修改数据的时候再去比较原理的数据是否是之前拿到的,如果是就修改,不是就把最高操作作废,重写拿数据修改。 线程安全,性能较好
线程使用乐观锁:Java中提供了乐观锁的原子类
5、线程通信(了解)
/**
* 线程通信,了解
*/
public class 线程通信 {
public static void main(String[] args) {
// 创建2个厨师线程(生产者)
Desk desk = new Desk();
//========================================================
new Thread(() -> {
while (true){
desk.put();
}
},"厨师1").start();
//========================================================
new Thread(() -> {
while (true){
desk.put();
}
},"厨师2").start();
//========================================================
// 创建2个吃货线程(消费者)
new Thread(() -> {
while (true){
desk.get();
}
}, "吃货1").start();
//========================================================
new Thread(() -> {
while (true){
desk.get();
}
}, "吃货2").start();
}
public static class Desk{
private List<String> list = new ArrayList<>();
//厨师做包子
public synchronized void put() {
try {
if (list.size() == 0){
String name = Thread.currentThread().getName();
list.add(name + "做的包子");
System.out.println(name + "做好了一个包子");
// 睡眠1秒
Thread.sleep(1000);
// 唤醒其他线程
this.notifyAll();
// 等待自己(等待自己的代码要写在最后面)
this.wait();
}else {
// 唤醒其他线程
this.notifyAll();
// 等待自己(等待自己的代码要写在最后面)
this.wait();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//吃包子
public synchronized void get() {
try {
String name = Thread.currentThread().getName();
if (list.size() ==1){
System.out.println(name + "吃了" + list.get(0));
list.clear();
// 睡眠1秒
Thread.sleep(1000);
// 唤醒其他线程
this.notifyAll();
// 等待自己(等待自己的代码要写在最后面)
this.wait();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
6、线程池
6.1、认识线程池
当线程池中的线程忙完当前的任务就会立马去处理其他任务,线程是复用的,这样就不会因为创建大量线程导致cpu飙升
6.2、创建线程池
public class ThreadPoolTest01 {
// public ThreadPoolExecutor(int corePoolSize,
// int maximumPoolSize,
// long keepAliveTime,
// TimeUnit unit,
// BlockingQueue<Runnable> workQueue,
// ThreadFactory threadFactory,
// RejectedExecutionHandler handler) {
public static void main(String[] args) {
// ThreadPoolExecutor这个类是线程池的实现类
// 但是他有很多参数,可以按住Ctrl点击ThreadPoolExecutor查看源码中的参数
new ThreadPoolExecutor(3,5,10,
// 4是任务队列的数量
TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
}
}
按住Ctrl键点击ThreadPoolExecutor就可以查看源码中有哪些参数了
6.3、线程池处理Runnable任务
public static void main(String[] args) {
// ThreadPoolExecutor这个类是线程池的实现类
// 但是他有很多参数,可以按住Ctrl点击ThreadPoolExecutor查看源码中的参数
ExecutorService pool = new ThreadPoolExecutor(3,5,10,
// 4是任务队列的数量
TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
Runnable target = new MyRunnable();
pool.execute(target);//线程池会自动创建一个新线程,自动处理这个任务,自动执行的!
pool.execute(target);//线程池会自动创建一个新线程,自动处理这个任务,自动执行的!
pool.execute(target);//线程池会自动创建一个新线程,自动处理这个任务,自动执行的!
pool.execute(target);//线程复用
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);//后面忙不过来就会创建;临时线程
pool.execute(target);
}
public static class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "===>666");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
6.4、Executors工具类实现线程池
6.5、多线程并发、并行、生命周期
网路编程
1、概述
2、网路通信三要素
2.1、IP
public class InetAddressTest {
public static void main(String[] args) throws Exception {
// 创建获取本机IP的对象
InetAddress ip1 = InetAddress.getLocalHost();
System.out.println(ip1.getHostName());
System.out.println(ip1.getHostAddress());
// 获取指定IP或者域名的IP地址对象。
InetAddress ip2 = InetAddress.getByName ("www.baidu.com");
System.out.println(ip2.getHostName());
System.out.println(ip2.getHostAddress() );
// 判断指定百度IP地址是否可达。就是ping命令
System.out.println(ip2.isReachable(3000));
}
}
2.2、端口号
2.3、协议
3、UDP通信
3.1、快速入门
/**
* 发送数据的客户端
*/
public class Client {
public static void main(String[] args) throws Exception {
// 1、创建客户端对象
DatagramSocket socket = new DatagramSocket();
//2、创建数据包对象封装要发出去的数据
/*参数一:封装要发出去的数据。
参数二:发送出去的数据大小(字节个数)参数三:。
*/
byte[] bytes ="我是快乐的客户端,我爱你abc".getBytes();
// bytes是封装要发出去的数据,bytes.length发送出去的数据大小(字节个数)
// InetAddress.getLocalHost(),6666服务端的IP地址(找到服务端主机>参数四:服务端程序的端口
DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(),6666);
//3、开始正式发送这个数据包的数据出去了
socket.send(packet);
System.out.println("客户端数据发送完毕~~~" );
socket.close(); //释放资源!
}
}
/**
* 服务端的类
*/
public class Server {
public static void main(String[] args) throws Exception {
// 1、创建一个服务端对象和注册端口
DatagramSocket socket = new DatagramSocket(6666);
// 2、创建一个接收数据包对象的容器大小
byte[] buffer = new byte[ 1024* 64]; // 64KB.
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
//3、开始正式使用数据包来接收客户端发来的数据
socket.receive(packet);
//4、从字节数组中,把接收到的数据直接打印出来
//接收多少就倒出多少
//获取本次数据包接收了多少数据。
int len = packet.getLength();
String rs = new String(buffer, 0, len);
System.out.println(rs);
//5、获取客户端的IP地址和端口号
InetAddress ip = packet.getAddress();
int port = packet.getPort();
System.out.println("客户端的IP地址:" + ip + ",客户端的端口号为:" + port);
socket.close();
}
}
先启动sever类,然后再启动client类就可以在sever类的控制台中看到接收的消息
3.2、多端多发
可以多个客户端发送消息
/**
* 发送数据的客户端
* 多端多发
*/
public class Client02 {
public static void main(String[] args) throws Exception {
// 1、创建客户端对象
DatagramSocket socket = new DatagramSocket();
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("请输入发送的消息...");
String msg = scanner.nextLine();
if (msg.equals("exit")){
System.out.println("客户端已退出~~~" );
socket.close(); //释放资源!
break;
}
byte[] bytes = msg.getBytes();
DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(),6666);
socket.send(packet);
}
}
}
/**
* 服务端的类
* 多端多发
*/
public class Server02 {
public static void main(String[] args) throws Exception {
// 1、创建一个服务端对象和注册端口
DatagramSocket socket = new DatagramSocket(6666);
// 2、创建一个接收数据包对象的容器大小
byte[] buffer = new byte[1024* 64]; // 64KB.
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
while (true) {
//3、开始正式使用数据包来接收客户端发来的数据
socket.receive(packet);
//4、从字节数组中,把接收到的数据直接打印出来
//接收多少就倒出多少
//获取本次数据包接收了多少数据。
int len = packet.getLength();
String rs = new String(buffer, 0, len);
System.err.println(rs);
//5、获取客户端的IP地址和端口号
InetAddress ip = packet.getAddress();
int port = packet.getPort();
System.out.println("客户端的IP地址:" + ip + ",客户端的端口号为:" + port);
}
}
}
4、TCP通信
4.1、快速入门
public class Client01 {
public static void main(String[] args) throws Exception {
Socket socket = new Socket("127.0.0.1", 8888);
// 获取Socket的输出流
OutputStream os = socket.getOutputStream();
// 将低级流包装成一个数据流
DataOutputStream dos = new DataOutputStream(os);
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("请输入发送的消息...");
String msg = scanner.nextLine();
if (msg.equals("exit")) {
System.out.println("客户端已退出~~~");
dos.close();
socket.close(); //释放资源!
break;
}
// 写入数据
dos.writeUTF(msg);
}
}
}
public class Server01 {
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(8888);
// 阻塞等待客户端连接
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
DataInputStream dis = new DataInputStream(inputStream);
while (true) {
try {
String s = dis.readUTF();
System.out.println(s);
} catch (Exception e) {
System.out.println("客户端已经离线了~~~");
dis.close();
socket.close();
break;
}
}
}
}
4.2、多客户端连接
/**
* 服务端接收多端消息
*/
public class Server02 {
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(8888);
while (true){
// 阻塞等待客户端连接
Socket socket = serverSocket.accept();
System.out.println("有人上线了:"+ socket.getRemoteSocketAddress());
new Thread(new ServerThread(socket)).start();
}
}
public static class ServerThread implements Runnable{
private Socket socket;
public ServerThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
InputStream inputStream = socket.getInputStream();
DataInputStream dis = new DataInputStream(inputStream);
while (true) {
try {
// 当读取数据的管道异常,说明客户端下线
String s = dis.readUTF();
System.out.println(socket.getRemoteSocketAddress() + ":" +s);
} catch (Exception e) {
System.out.println("有人下线了:"+ socket.getRemoteSocketAddress());
dis.close();
socket.close();
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
4.3、群聊
**
* 群聊客户端
*/
public class Client {
public static void main(String[] args) throws Exception {
Socket socket = new Socket("127.0.0.1", 8888);
// 获取Socket的输出流
OutputStream os = socket.getOutputStream();
// 将低级流包装成一个数据流
DataOutputStream dos = new DataOutputStream(os);
new Thread(new ClientReader(socket)).start();
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("请输入发送的消息...");
String msg = scanner.nextLine();
if (msg.equals("exit")) {
System.out.println("客户端已退出~~~");
dos.close();
socket.close(); //释放资源!
break;
}
// 写入数据
dos.writeUTF(msg);
}
}
public static class ClientReader implements Runnable{
private Socket socket;
public ClientReader(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
while (true) {
try {
InputStream inputStream = socket.getInputStream();
DataInputStream dis = new DataInputStream(inputStream);
String s = null;
try {
s = dis.readUTF();
System.out.println(s);
} catch (Exception e) {
System.out.println("服务器出问题了");
dis.close();
socket.close();
break;
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
}
/**
* 群聊服务端
*/
public class Server {
private static List<Socket> socketList = new ArrayList<>();
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(8888);
while (true){
// 阻塞等待客户端连接
Socket socket = serverSocket.accept();
socketList.add(socket);
System.out.println("有人上线了:"+ socket.getRemoteSocketAddress());
new Thread(new ServerThread(socket)).start();
}
}
public static class ServerThread implements Runnable{
private Socket socket;
public ServerThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
InputStream inputStream = socket.getInputStream();
DataInputStream dis = new DataInputStream(inputStream);
while (true) {
try {
// 当读取数据的管道异常,说明客户端下线
String s = dis.readUTF();
// 发送给所有客户端
sendMsgAll(s);
System.out.println(socket.getRemoteSocketAddress() + ":" +s);
} catch (Exception e) {
socketList.remove(socket);
System.out.println("有人下线了:"+ socket.getRemoteSocketAddress());
dis.close();
socket.close();
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 发送消息到所有客户端
*/
private void sendMsgAll(String msg) throws Exception {
for (Socket socket: socketList){
OutputStream outputStream = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(outputStream);
dos.writeUTF(msg);
dos.flush();
}
}
}
}
启动多个客户端就可以实现群聊效果了
4.4、实现BS架构
/**
* BS服务端
*/
public class BSServer {
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(8080);
while (true){
// 阻塞等待客户端连接
Socket socket = serverSocket.accept();
new Thread(new ServerThread(socket)).start();
}
}
public static class ServerThread implements Runnable{
private Socket socket;
public ServerThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
OutputStream os = socket.getOutputStream();
PrintStream ps= new PrintStream(os);
try {
ps.println("HTTP/1.1 200 OK");
ps.println("Content-Type:text/html; charset=UTF-8");
ps.println(); //必须换行
ps.println("网络编程666");
ps.println("<div style='color:red;'>JavaSE</div>");
} catch (Exception e) {
ps.close();
socket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
启动之后在浏览器输入http://127.0.0.1:8080。就可以看到响应在浏览器的内容了
IO流
1、File
1.1、创建File
public class FileTest01 {
public static void main(String[] args) {
// 3种路径写法
File f1 = new File("F:\\lintellij_code\\javase\\src\\main\\java\\io\\file\\a.txt");
File f2 = new File("F:/lintellij_code/javase/src/main/java/io/file/a.txt");
// f1.separator是调用系统的分隔符
File f3 = new File("F:"+ f1.separator + "/lintellij_code/javase/src/main/java/io/file/a.txt");
// 使用相对路径
File f4 = new File("src/main/java/io/file/a.txt");
System.out.println("f1.length():"+f1.length());
System.out.println("f2.length():"+f2.length());
System.out.println("f3.length():"+f3.length());
System.out.println("f4.length():"+f4.length());
// 可以指向文件和文件夹,也可以指向不存在的文件夹
File f5 = new File("F:\\lintellij_code\\javase\\src\\main\\java\\io\\file");
System.out.println("f5.length():"+f5.length()); // f5.length()是文件夹本身的大小
System.out.println("f5.exists():"+f5.exists());
}
}
推荐使用文件的相对路径,即src/...
1.2、File常用方法
1.3、前置知识
1.3.1、方法递归
1.3.2、文件搜索
public class FileSearch {
public static void main(String[] args) throws IOException {
searchFile(new File("E:/qq软件/Bin"), "QQScLauncher.exe");
}
public static void searchFile(File dir, String fileName) throws IOException {
// 1、把非法的情况都拦截住
if(dir == null || !dir.exists() || dir.isFile()) {
return; //代表无法搜索
}
// 2、dir不是null,存在,一定是目录对象。
// 获取当前目录下的全部一级文件对象。
File[ ] files = dir.listFiles();
// 3、判断当前目录下是否存在一级文件对象,以及是否可以拿到一级文件对象。
if(files != null && files.length > 0){
// 4、遍历全部一级文件对象。
for (File f : files) {
//5、判断文件是否是文件,还是文件夹
if(f.isFile()){
//是文件,判断这个文件名是否是我们要找的
if(f.getName().contains(fileName)) {
System.out.println("找到了:" + f.getAbsolutePath());
//启动exe文件
Runtime runtime = Runtime.getRuntime();
runtime.exec(f.getAbsolutePath());
}
}else {
//是文件夹,继续重复这个过程〈递归)
searchFile(f, fileName);
}
}
}
}
}
1.3.3、字符集
如果在idea中使用GBK编码UTF-8解码,就会出现乱码
2、IO流体系
输入流是要内存输入数据,输出流是内存将数据写到磁盘中
3、字节流
3.1、FileInputStream(文件字节输入流)
public class FileInputStreamTest01 {
public static void main(String[] args) throws Exception {
/**
* 一次读取一个字节
*/
// 两者路径写法
// 1.创建文件字节输入流管道,于源文件接触
// InputStream fileInputStream = new FileInputStream(new File("src/main/java/io/file/a.txt"));
InputStream fileInputStream = new FileInputStream("src/main/java/io/file/a.txt");
// 2.开始读取文件的字节数据
// int a = fileInputStream.read();
// System.out.println((char) a);
// 调用系统资源来读取文件字节数据,性能极差,read()中文乱码,调用完要释放资源
int b;
while ((b = fileInputStream.read()) != -1){
System.out.print((char) b);
}
// 释放资源
fileInputStream.close();
/**
* 一次读取多个字节
*/
// 一次读取多个字节
InputStream is = new FileInputStream("src/main/java/io/file/a.txt");
//2、开始读取文件中的字节数据:每次读取多个字节。
byte[] buffer = new byte[3];
//将字节读取到buffer桶中
int len = is.read(buffer);
//将buffer桶中的字节转换成字符串
String rs = new String(buffer);
System.out.println(rs);
System.out.println("当次读取的字节数量:" + len);
// 当次读取的字节数量: 3
// buffer = [abc]
// buffer = [66c]
int len2 = is.read(buffer);
//注意:读取多少,倒出多少。
String rs2 = new String(buffer,0, len2);
System.out.println(rs2);
System.out.println("当次读取的字节数量: " + len2);
int len3 = is.read(buffer);
System.out.println(len3); // -1
//3、使用循环改造。
byte[] buffer1 = new byte[3]; // 每次读取3个字节(相当于桶)下次读取
int len1;
//记住每次读取了多少个字节。abc 66
while ((len1 = is.read(buffer)) != -1){
//注意:读取多少,倒出多少。
String rs1 = new String(buffer1,0 , len1);
System.out.print(rs1);
}
is.close();
//性能得到了明显的提升!!
//这种方案也不能避免读取汉字输出乱码的问题!!
/**
* 一次读整个文件的字节。文件字节输入流
*/
InputStream is3 = new FileInputStream("src/main/java/io/file/a.txt");
// File file3 = new File("src/main/java/io/file/a.txt");
// 1、获取文件的字节数
// long size = file3.length();
//2、开始读取文件中的字节数据:每次读取多个字节。
// byte[] buffer = new byte[(int) size];
byte[] buffer3 = is.readAllBytes(); //两者获取文件字节长度的方法
//将字节读取到buffer桶中
is3.read(buffer3);
//将buffer桶中的字节转换成字符串
String str = new String(buffer3);
System.out.println(str);
is3.close();
}
}
3.2、FileOutputStream(文件字节输出流)
/**
* 字节输出流
*/
public class FileOutputStreamTest01 {
public static void main(String[] args) throws Exception {
//创建字符输出流通道(桶)覆盖式的写入,后面加了true参数,表示追加写入(非覆盖写入)
// 路径不存在,则会自动创建
OutputStream os = new FileOutputStream("src/main/resources/io-file/out.txt",true);
os.write(97); //97是字符a的ASCII码
os.write("我爱Java----abc".getBytes()); //按照字符的字节写入
os.write("我爱Java".getBytes(), 0, 6); //按照字符的字节写入,从第0个字节开始,6个字节写入。
os.write("\r\n".getBytes()); //添加换行符。\r支持各种平台,\n只支持windows平台
os.close();
}
}
案例
/**
* 文件复制案例
*/
public class FileCopy {
public static void main(String[] args) throws Exception {
InputStream is = null;
OutputStream os = null;
try {
is = new FileInputStream("src/main/resources/io-file/a.txt");
os = new FileOutputStream("src/main/resources/io-file/a_copy.txt");
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer))!= -1) {
os.write(buffer, 0, len);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
// 关闭流,先创建的要后关闭
os.close();
is.close();
}
System.out.println("文件复制成功");
}
}
4、释放资源的方式
防止代码出现异常,资源无法释放
public class FileCopy {
public static void main(String[] args) throws Exception {
// InputStream is = null;
// OutputStream os = null;
//
// try {
System.out.println(10/0);
// is = new FileInputStream("src/main/resources/io-file/a.txt");
// os = new FileOutputStream("src/main/resources/io-file/a_copy.txt");
// byte[] buffer = new byte[1024];
// int len;
// while ((len = is.read(buffer))!= -1) {
// os.write(buffer, 0, len);
// }
// } catch (IOException e) {
// throw new RuntimeException(e);
// } finally {
// // 关闭流,先创建的要后关闭
// if (os != null) os.close();
// if (os != null) is.close();
// }
//
// System.out.println("文件复制成功");
// }
/**
* 推荐使用try-with-resources释放资源
*/
try(
InputStream is = new FileInputStream("src/main/resources/io-file/a.txt");
OutputStream os = new FileOutputStream("src/main/resources/io-file/a_copy.txt")
)
{
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer))!= -1) {
os.write(buffer, 0, len);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
5、字符流
字符流非常适合做文件的读写
5.1、FileReader(文件字符输入流)
/**
* 字节输出流
*/
public class FileOutputStreamTest01 {
public static void main(String[] args) throws Exception {
try(Reader os = new FileReader("src/main/resources/io-file/out.txt")) {
/**
* 一个字符一个字符的读取
*/
int c ;
while ((c=os.read())!= -1) {
System.out.print((char) c); // 将ASCII码转为字符
}
/**
* 多个字符读取
*/
char[] buffer = new char[3];
int len;
while ((len=os.read(buffer))!= -1) {
//将字节转换为字符
System.out.print(new String(buffer, 0, len));
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
5.2、FileWriter(文件字符输出流)
public class FileWired {
public static void main(String[] args) {
// 参数append:true表示追加写入
try(Writer wf = new FileWriter("src/main/resources/io-file/writer.txt", true)) {
wf.write("a"); // 写入一个字符
wf.write("中文");
wf.write(97); // 根据ASCII码写入一个字符
wf.write("abc中文"); // 写入一个字符串
wf.write("abc中文",0,4); // 写入字符串的一部分
wf.write("\r\n"); // 写入一个换行符
char[] chars = {'a', 'b', 'c', '加', '瓦'};
wf.write(chars); // 写入一个字符数组
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
6、缓冲流
6.1、字节缓冲流
字节缓冲流有BufferedInputtStream字节缓冲输入流和BufferedOutputStream字节缓冲输出流
/**
* 字节缓冲流
*/
public class BufferedTest {
public static void main(String[] args) throws Exception {
try(
InputStream is = new FileInputStream("src/main/resources/io-file/a.txt");
InputStream bis = new BufferedInputStream(is);
OutputStream os = new FileOutputStream("src/main/resources/io-file/a_copy.txt");
// 默认的缓冲区大小是8k,也可以设置比如9k
OutputStream bos = new BufferedOutputStream(os, 1024*9);
)
{
byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer))!= -1) {
bos.write(buffer, 0, len);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
6.2、BufferedReader(字符缓冲流)
/**
* 字符缓存输入流
*/
try(
Reader fr = new FileReader("src/main/resources/io-file/a.txt");
// 包装流,包装一个字符流
BufferedReader br = new BufferedReader(fr);
// 默认的缓冲区大小是8k,也可以设置比如9k
)
{
byte[] buffer = new byte[1024];
String str;
while ((str = br.readLine()) != null) {
System.out.println(str);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
6.3、BufferedWriter字节输出流
6.4.案例
public class BufferedCharDemo {
public static void main(String[] args) {
try(
FileReader fr = new FileReader("src/main/resources/io-file/出师表.txt");
FileWriter fw = new FileWriter("src/main/resources/io-file/出师表-copy.txt");
BufferedReader br = new BufferedReader(fr);
BufferedWriter bw = new BufferedWriter(fw)
) {
List<String> data = new ArrayList<>();
String str;
while ((str = br.readLine()) != null){
data.add(str);
}
// 按照元素的第首数字排序
Collections.sort(data);
for (String line : data){
bw.write(line);
bw.newLine();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
7、转换流
/**
* 字符输入流转换为字节输入流
*/
public class InputStreamReaderTest {
public static void main(String[] args) {
try(
InputStream fis = new FileInputStream("src/main/resources/io/转换流.txt");
// 转换流,将字节流转换为字符流
InputStreamReader isr = new InputStreamReader(fis, "GBK");
BufferedReader br = new BufferedReader(isr)
) {
String line;
while ((line = br.readLine())!= null) {
System.out.println(line);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意编码的方式要和文件的编码一样
/**
* 字符输出流转换为字节输出流
*/
public class OutputStreamWriterTest {
public static void main(String[] args) {
try(
OutputStream fos = new FileOutputStream("src/main/resources/io/转换流.txt");
// 转换流,将字节流转换为字符流
OutputStreamWriter osw = new OutputStreamWriter(fos, "GBK");
BufferedWriter bw = new BufferedWriter(osw)
) {
bw.write("加瓦太难了");
} catch (Exception e) {
e.printStackTrace();
}
}
}
8、打印流
打印流几乎可以替代前面所有的写操作(这是一个字节输出流),且自带缓冲流,性能较好
其实System.out.println()语句就是打印流,默认打印位置是控制台
9、数据流
/**
* 数据输出流
*/
public class DataOutputStreamTest {
public static void main(String[] args) {
String filePath = "src/main/resources/io/数据流.txt";
try(DataOutputStream dos = new DataOutputStream(new FileOutputStream(filePath))) {
dos.writeInt( 97);
dos.writeDouble(99.5);
dos.writeBoolean( true);
dos.writeUTF("这是中文");
} catch (Exception e) {
e.printStackTrace();
}
}
}
这不是乱码,只是方便下次读取
/**
* 数据输出流
*/
public class DataInputStreamTest {
public static void main(String[] args) {
String filePath = "src/main/resources/io/数据流.txt";
try(DataInputStream dos = new DataInputStream(new FileInputStream(filePath))) {
System.out.println(dos.readInt());
System.out.println(dos.readDouble());
System.out.println(dos.readBoolean());
System.out.println(dos.readUTF());
} catch (Exception e) {
e.printStackTrace();
}
}
}
写和读的操作应该一一对应,否则报错
10、序列化流
对象序列化:把Java对象写入到文件中去
对象反序列化:把文件里的Java对象读出来
// 需要序列化的对象一定要实现Serializable接口
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
private String loginName;
private String username;
private int age;
// transient关键字修饰的变量不参与序列号
private transient String password;
}
/**
* 序列化对象
*/
public class ObjectOutputStreamTest {
public static void main(String[] args) {
String path = "src/main/resources/io流/序列号流.txt";
try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path))) {
User user = new User("admin", "张三", 32, "java666");
// 序列号对象到文件中
oos.writeObject(user);
System.out.println("对象序列号成功");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
/**
* 反序列化
*/
public class ObjectInputStreamTest01 {
public static void main(String[] args) {
String path = "src/main/resources/io流/序列号流.txt";
try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path))) {
// 序列号对象到文件中
ObjectOutputStreamTest01.User u = (ObjectOutputStreamTest01.User) ois.readObject();
System.out.println(u);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
11、IO框架
日志
1、概述
2、Logback日志
2.1、Logback日志介绍
2.2、Logback日志快速入门
导入logback依赖坐标
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.12</version>
</dependency>
我的是maven项目,在resources目录下新建logback.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 控制台输出Appender -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- 日志输出格式 -->
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- 文件输出Appender -->
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<!-- 日志文件路径 -->
<file>F:/lintellij_code/javase/src/main/resources/logback.log</file>
<append>true</append>
<encoder>
<!-- 日志输出格式 -->
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!--指定日志文件拆分和压缩规则-->
<rollingPolicy class="ch.qos.logback.core.rolling.sizeAndTimeBasedRollingPolicy">
<!--通过指定压缩文件名称,来确定分割文件方式,文件到达1MB就压缩,文件名格式为:logback-年-月-日.log.gz-->
<fileNamePattern>F:/lintellij_code/javase/src/main/resources/logback-%i-%d{yyyy-MM-dd}-.log.gz</fileNamePattern>
<!--文件拆分大小-->
<maxFilesize>1MB</maxFilesize>
</rollingPolicy>
</appender>
<!-- 指定包(com.example)的日志级别为DEBUG -->
<logger name="com.example" level="DEBUG" />
<!-- 根Logger,info表示只输出info及以上级别的日志,all表示将root标签下的所有日志都输出,-->
<root level="info">
<!-- 控制台输出-->
<appender-ref ref="CONSOLE" />
<!-- 文件输出-->
<appender-ref ref="FILE" />
</root>
</configuration>
编写测试代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogBackTest {
// 创建一个日志对象,LogBackTest.class是这个日志对象的名字
private final static Logger LOGBACK = LoggerFactory.getLogger(LogBackTest.class);
public static void main(String[] args) {
try {
LOGBACK.info("开始执行1/0");
int a = 1/0;
LOGBACK.info("1/0执行完了");
} catch (Exception e) {
LOGBACK.error("1/0出bug了~~~");
}
}
}
运行完后就可以看到日志信息保存到了logback.log文件中了
2.3、核心配置文件logback.xml
面向对象
1、枚举
public class EnumTest {
public static void main(String[] args) {
System.out.println("code:" + E.A.getCode() + ",msg:" + E.A.getMsg());
System.out.println(E.C);
System.out.println("年龄:" + E.age);
}
public enum E {
// 枚举成员变量,要按照类似构造函数的形式进行初始化,然后使用get方法获取里面的值
A(1, "hello"),
B(2, "world"),
C;
// 常规变量
public static int age = 22;
private int code;
private String msg;
// 构造方法
E(int code, String msg) {
this.code = code;
this.msg = msg;
}
E(){}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
}
2.、泛型
定义类、接口、方法时,同时声明了一个或者多个类型变量(如:<E>),称为泛型类、泛型接口,泛型方法、它们统称为泛型,
问题和注意事项:
泛型的擦除泛型是工作在编译阶段的,一旦程序编译成class文件,class文件中就不存在泛型了,这就是泛型擦除。
泛型不支持基本数据类型,只能支持对象类型(引用数据类型)。
public class FanXingTest {
public static void main(String[] args) {
Fanxing<String,Integer> fanxing = new Fanxing<>();
fanxing.add1("a", 1);
fanxing.add2(5);
ArrayList<StringNumber> list = new ArrayList<>();
fanxing.add3(list);
}
public static class Fanxing<E,F>{
public void add1(E e,F f){
System.out.println(e + "e是java类的泛型" + f);
}
public <A> void add2(A e){
System.out.println("e是自定义泛型方法:"+ e.getClass());
}
// ?泛型通配符,但是一定是Number及其的子类
public void add3(ArrayList<? extends Namber> list){
System.out.println(list);
}
}
public static class Namber{}
public static class StringNumber extends Namber{}
}
异常Exception
入门异常
编译时的异常,IDEA会报红:
可以加 throws ParseException 将异常抛给上一层(main方法的上一层,即jvm),jvm捕捉异常后打印到控制台,也可以使用 try-catch 包围,选中之后 ctrl + alt + t
自定义运行时异常
public class exceptionTest02 {
public static void main(String[] args) {
// 使用try-catch语句来捕获异常
try {
saveAge(10);
System.out.println("底层执行常规");
} catch (Exception e) {
// 打印异常信息
e.printStackTrace();
System.err.println("底层执行常规");
}
}
public static void saveAge(int age){
if (age > 0 && age < 150){
System.out.println("年龄被保存了---" + age);
}else {
//使用自定义异常类
// throw 关键字将异常对象抛出给上层
throw new AgeIllegalRuntimeException("年龄不合法");
}
}
// 自定义异常类,继承RuntimeException,
public static class AgeIllegalRuntimeException extends RuntimeException{
// 重写构造器
public AgeIllegalRuntimeException(String msg) {
super(msg);
}
}
}
自定义编译时异常
public class exceptionTest03 {
public static void main(String[] args) {
// 使用try-catch语句来捕获异常,不抛异常idea就会爆红
try {
saveAge(10);
System.out.println("底层执行常规");
} catch (Exception e) {
// 打印异常信息
e.printStackTrace();
System.err.println("底层执行常规");
}
}
public static void saveAge(int age) throws AgeIllegalException {
if (age > 0 && age < 150){
System.out.println("年龄被保存了---" + age);
}else {
//使用自定义异常类
// throw 关键字将异常对象抛出给上层
throw new AgeIllegalException("年龄不合法");
}
}
// 自定义编译时异常类,继承Exception,
public static class AgeIllegalException extends Exception{
// 重写构造器
public AgeIllegalException(String msg) {
super(msg);
}
}
}
错误严重的就使用编译时异常,错误不严重的就使用运行时异常
异常处理的两种方式
1、埔获异常,记录异常苷响应合适的信息给用户
public class exceptionTest04 {
public static void main(String[] args) {
try {
test();
} catch (Exception e) {
System.out.println("您当前操作有问题");
e.printStackTrace();
}
}
//全部异常都可以用Exception来抛Exception是所有异常的祖宗
public static void test() throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
Date date = sdf.parse("2023-12-15 9:02");
System.out.println(date);
f();
}
public static void f() throws Exception {
// 读取文件的
InputStream inputStream = new FileInputStream("D/meinv.png");
}
}
2、捕获异常,尝试重新修复
public class exceptionTest05 {
public static void main(String[] args) {
//需求:调用一个方法,让用户输入一个合适的价格返回为止
// 尝试修复,即使输入文字系统也不会崩
while (true) {
try {
System.out.println(getMoney());
break;
}catch (Exception e) {
System.out.println("请您输入合法的数字!!");
}
}
}
public static double getMoney(){
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请您输入合适的价格:");
double money = sc.nextDouble();
if(money >= 0){
return money;
}else {
System.out.println("您输入的价格是不合适的");
}
}
}
}
反射
1、认识反射
对加载在内存中的类进行操作
2、获取class对象(字节码对象)
/**
* 获取class对象
*/
public class GetClassTest {
public static void main(String[] args) throws Exception {
// 获取class对象
Class c1 = Student.class; // 全类名
System.out.println(c1.getName());
Class c2 = Class.forName("反射.Student");
System.out.println(c1==c2);
Student student = new Student();
Class c3 = student.getClass();
System.out.println(c2==c3);
}
}
3、获取Class的构造器对象
/**
* 获取构造器并使用
*/
public class GetConstructor {
public static void main(String[] args) throws Exception {
//1、反射第一步:必须先得到这个类的Class对象
Class c = Student.class;
//2、获取某个构造器:无参数构造器
// constructor constructor = c.getConstructor( );
Constructor constructor1 = c.getDeclaredConstructor();
System.out.println(constructor1.getName() + "--->"+ constructor1.getParameterCount());
// 创建对象
Student s1 = (Student) constructor1.newInstance();
System.out.println(s1);
//3、获取有参数构造器
Constructor constructor2 = c.getDeclaredConstructor(String.class, int.class);
System.out.println(constructor2.getName() + "--->" + constructor2.getParameterCount());
//4、创建对象
constructor2.setAccessible(true); // 禁止检查访问权限
Student s2 = (Student) constructor2.newInstance("张三", 20);
System.out.println(s2);
}
}
4、获取成员变量
/**
* 获取成员变量
*/
public class GetField {
public static void main(String[] args) throws Exception {
//1、反射第一步:必须是先得到类的Class对象
Class c = Cat.class;
//2、获取类的全部成员变量。
Field[] fields = c.getDeclaredFields();
//3、遍历这个成员变量数组
for (Field field : fields) {
System.out.println(field.getName() +"--->"+ field.getType());
}
//4、定位某个成员变量
Field fName = c.getDeclaredField("name");
System.out.println(fName. getName() + "--->" + fName.getType());
Field fAge = c.getDeclaredField("age" );
System.out.println(fAge.getName() + "--->" + fAge.getType());
}
}
5、获取成员方法
/**
* 获取成员方法并执行
*/
public class GetMethods {
public static void main(String[] args) throws Exception {
//1、反射第一步:先得到Class对象。
Class c = Cat.class;
//2、获取类的全部成员方法。
Method[] methods = c.getDeclaredMethods();
//3、遍历这个数组中的每个方法对象
for (Method method : methods) {
System.out.println(method.getName() + "--->" +method.getParameterCount()+"---->"+method.getReturnType());
}
//4、获取某个方法对象
Method run = c.getDeclaredMethod("run");
Method p = c.getDeclaredMethod("p", String.class);
//拿run方法,无参数的
System.out.println(run. getName() + "--->"+run.getParameterCount() + "_--->"+run.getReturnType());
System.out.println(p. getName() + "--->"+p.getParameterCount() + "_--->"+p.getReturnType());
Cat cat = new Cat();
//调用无参数的run方法,用cat对象触发调用的
Object rs = run.invoke(cat); // rs是执行方法的返回结果
System.out.println("run方法的返回值:" + rs);
p.setAccessible(true); //禁止检查访问权限
Object s = p.invoke(cat,"aa");
System.out.println(s);
}
}
6、使用反射制作Java框架
@Data
@AllArgsConstructor
@NoArgsConstructor
class Student {
private String name;
private int age;
private char sex;
private double height;
private String hobby;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private String name;
private double salary; //薪水
}
/**
* 目标:保存任意对象的字段和其数据到文件中去
*/
public class ObjectFrame {
public static void saveObject(Object obj) throws Exception {
String filePath = "src/main/java/反射/制作简易框架案例/data.txt";
PrintStream ps = new PrintStream(new FileOutputStream(filePath,true));
Class c = obj.getClass();
String cName = c.getSimpleName(); // 获取类名
ps.println("---------------" + cName + "----------------");
//2、从这个类中提取它的全部成员变量
Field[] fields = c.getDeclaredFields();
//3、遍历每个成员变量。
for (Field field : fields) {
//4、拿到成员变量的名字
String name = field.getName();
//5、拿到这个成员变量在对象中的数据。
field.setAccessible(true); //禁止检查访问控制
String value = field.get(obj) +"";
//6、写入文件
ps.println(name + "=" + value);
}
ps.close();
}
}
public class FrameTest {
public static void main(String[] args) throws Exception {
Student s1 = new Student("小飞吴彦祖", 18, '男', 180, "篮球");
Teacher t1 = new Teacher("小美",50000);
ObjectFrame.saveObject(s1);
ObjectFrame.saveObject(t1);
}
}
注解
1、自定义注解
/**
* 自定义注解
*/
public @interface MyAnnotation {
String a();
boolean b() default true;
String[] c();
}
@MyAnnotation(a = "张三", c={"1", "2", "3"})
public class AnnotationTest {
@Test
@MyAnnotation(a = "李四", b = false, c={"HTML", "CSS"})
public void test() {
System.out.println("test");
}
}
2、注解的原理
运行代码就可以生成AnnotationTest类的class文件
然后再这个目录中cmd,输入命令>javap class文件名(不带后缀)我是:javap MyAnnotation
进行反编译:
里面都是抽象方法
3、元注解
4、注解的解析
5、注解的应用,模拟Junit框架
/**
* 自定义注解
*/
@Target({ElementType.TYPE, ElementType.METHOD}) // 注解的作用范围是类和方法上
@Retention(RetentionPolicy.RUNTIME) // 注解的生命周期是运行时
public @interface MyTest4 {
String value();
double aaa() default 100;
String[] bbb();
}
/**
* 使用注解
*/
@MyTest4(value = "老六", aaa = 10,bbb = {"111", "222"})
public class Demo {
@MyTest4(value = "老7", aaa = 15,bbb = {"333", "444"})
public void test(){
System.out.println("这是一个方法");
}
}
/**
* 解析注解
*/
public class AnnotationTest {
public static void main(String[] args) throws Exception {
//1、先得到Class对象
Class c = Demo.class;
//2、解析类上的注解
//判断类上是否包含了某个注解
if(c.isAnnotationPresent(MyTest4.class)){
//获取全部注解
MyTest4 myTest4 = (MyTest4) c.getDeclaredAnnotation(MyTest4.class);
// 获取注解的值
System.out.println(myTest4.value());
System.out.println(myTest4.aaa());
System.out.println(Arrays.toString(myTest4.bbb()));
}
System.out.println("------------------------");
//解析方法上的注解
Method m = c.getDeclaredMethod("test");
if (c.isAnnotationPresent(MyTest4.class)){
MyTest4 myTest4 = (MyTest4) m.getDeclaredAnnotation(MyTest4.class);
System.out.println(myTest4.value());
System.out.println(myTest4.aaa());
System.out.println(Arrays.toString(myTest4.bbb()));
}
}
}
junit框架中的@Test注解在IDEA中是会有启动按钮的,而我的模拟的注解是没有的,那是因为我没有和IDEA的公司打电话,嘿嘿!
/**
* 自定义注解
*/
@Target(ElementType.METHOD) //注解只能注解方法
@Retention(RetentionPolicy.RUNTIME) //让当前注解可以一直存活着
public @interface MyTest {
}
/**
*目标:模拟Junit框架的设计。
*/
public class AnnotationTest {
public void test1(){
System.out.println( " ===test1====" );
}
@MyTest
public void test2(){
System.out.println( "===test2====");
}
public static void main(String[] args) throws Exception {
AnnotationTest a = new AnnotationTest();
//启动程序!
//1、得到Class对象
Class c = AnnotationTest.class;
//2、提取这个类中的全部成员方法
Method[ ] methods = c.getDeclaredMethods();
//3、遍历这个数组中的每个方法,看方法上是否存在@MyTest注解,存在
// 触发该方法执行。
for (Method method : methods) {
if(method.isAnnotationPresent(MyTest.class)){
//说明当前方法上是存在@MyTest触发当前方法执行。
method.invoke(a);
}
}
}
}
动态代理
/**
* 给中介公司用的代理接口
*/
public interface Star {
String sing(String name);
void dance();
}
**
* 大明星类
*/
public class BigStar implements Star{
private String name;
public BigStar (String name) {
this.name = name;
}
// 唱歌
@Override
public String sing (String name){
System.out.println(this.name + "正在唱: " +name) ;return "谢谢!谢谢! ";
}
// 跳舞
@Override
public void dance(){
System.out.println(this.name+"正在优美的跳舞~~");
}
}
/**
* 代理公司
*/
public class ProxyUtil {
public static Star createStar(BigStar bigStar){
/** Proxy.newProxyInstance方法的参数:他可以创建一个代理对象
* ClassLoader loader,
* Class<?>[] interfaces,
* InvocationHandler h
*/
//一般在主程序中这样调用:
// Star starProxy = ProxyUtil.createProxy(s);
// StarProxy.sing("好日子") starProxy.dance();
// 1. 动态代理
Star starProxy = (Star) Proxy.newProxyInstance(
ProxyUtil.class.getClassLoader(), new Class[]{Star.class}, new InvocationHandler() {
/**
* 代理对象调用方法时,会调用invoke方法
* Object proxy:当前调用对象:starProxy
* Method method:当前调用的方法
* Object[] args:当前调用方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("sing")){
System.out.println("准备话筒,收钱200万");
} else if (method.getName().equals("dance")) {
System.out.println("准备场地,收钱500万");
}
// 2. 调用目标对象的方法
return method.invoke(bigStar, args);
}
});
return starProxy;
}
}
public class Test {
public static void main(String[] args) {
BigStar bigStar = new BigStar("杨超越");
Star star = ProxyUtil.createStar(bigStar);
String r = star.sing("好日子");
System.out.println(r);
star.dance();
}
}