前言
学过 C/C++, 所以最基础部分就跳过啦~
基础知识
Java 是一门高级编程语言。Sun 于 1995 年,后被 Oracle
收购。
Java 技术体系:Java SE: 标准版、Java EE: 企业版、Java ME:
小型版
JDK: Java Development Kit 即开发工具包,目前常用的为
JDK-8,JDK-17 (Leetcode 的 Java 环境就是 17)
javac
是个编译工具,java
是个执行工具
.java
源代码 --(javac编译)--> .class
字节码文件 --> 使用 java 运行
jdk 11 可以直接使用 java 命令运行,后台会自动编译运行
JVM: Java Virtual Machine, 即 Java 虚拟机,运行 Java
程序的地方
JRE: Java Runtime Environment, Java 运行环境
JVM,JRE,核心类库 包含在 JDK 中
跨平台性:class 文件在不同平台的 JVM 上运行
数据类型
基本数据类型:4类8种 - 整型(byte, short, int, long),浮点型(float,
double), 字符型,布尔型
应用数据类型:字符串(String)
类型转换
自动类型转换:范围小 --> 范围大
表达式自动类型转换:范围小 --> 范围大,结果取决于最高类型
表达式中 byte, short, char 直接强制转换为int参与运算
强制类型转换:一般是大范围 --> 小范围
运算符
基本算数运算符:+,-,*,,%
自增自减运算:++,--、
赋值运算符:=,+=,-=,*=,/=,%=
关系运算符:>=, <=, >, <, ==, !=
逻辑运算符:&,|,!,^,&&,||
&& 与 &,| 与 ||
的区别是单个的双边都会执行,双倍的有短路现象
&& 优先级高于 ||
位运算:>> 和 <<
程序流程控制
分支语句
if () {} else if () {} else {}
switch () { case v1: break; default: ;}
switch 只支持
byte, short, int, char, String 不支持 long, float, double
循环结构
for(;;) {} 循环
while () {} 循环
do {} while() 循环
控制关键字:continue,break
数组
1 2 3 4 5 6 7 8 9 10 11 int [] s = new int []{1 , 2 , 3 , 4 };int [] s = {1 , 2 , 3 , 4 };int s[] = {1 , 2 , 3 , 4 }; int [] s = new int [12 ]; int n = s.length;for (int i = 0 ; i < n; ++i) { System.out.println(s[i]); }
数组变量名存储的是数组在内存的地址,是一种引用数据类型
Java 内存分配
方法区:.class 字节码文件
栈:方法运行使用
堆:new 的东西
方法
1 2 3 修饰符 返回值类型 方法名(形参表) { 代码; }
参数传递:值传递
方法重载:方法名称相同,形参列表不同(不同关心形参具体名称)
对象
面向对象编程:万物皆对象
三大特征:封装、继承、多态
一个java文件只能有一个 public 修饰的 class
类,且该类的类名必须与文件名相同
构造方法:
一个无返回值,与类名相同的方法,可以重载
没有构造方法会默认生成个无参数的构造方法
存在有参构造方法就没有默认的无参构造方法了
static
:修饰变量或方法,表示类变量或类方法(静态变量、静态方法),同样保存的内存中的堆中
单例设计模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class SingleInstance { public static void main (String[] args) { SingleInstance si = SingleInstance.get(); System.out.println(si); } private SingleInstance () { System.out.println("一个单例模式实例" ); } private static final SingleInstance si = new SingleInstance (); public static SingleInstance get () { return si; } }
继承
子类可以继承父类的非私有成员
1 2 3 public class B extends A { }
权限修饰符
修饰符
当前类
同一package下其他class
其他包的子类中
其他包其他类
private
✓
缺省
✓
✓
protected
✓
✓
✓
public
✓
✓
✓
✓
方法可以重写,private,static 修饰的成员不能重写
子类访问成员:就近有原则
访问父类成员:使用 super 关键字
子类构造函数会默认调用父类无参构造函数,如果没有无参构方法,必须手动调用
多态
对象多态、行为多态:即父类可以指向子类对象,但表现方法不同
技巧:编译看左边,执行看右边(但是成员变量只看左边)
用处:父类变量作为形参,可以接受子类对象,但是要调用子类特有方法需要强制类型转换
1 2 3 4 5 A a = new C (); if (a instanceof B) { B b2 = (B)a; }
final
修饰类:不能被继承
修饰方法:不能被重写
修饰变量:不能修改、必须初始化
抽象类
即 abstract 修饰的类或方法
abstract
修饰的方法只有声明,没有具体实现,且有抽象方法只能再抽象类中
抽象类不能实例化,只能继承,且子类重写所有抽象方法才能实例化,不然也是抽象类
接口类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public interface InterfaceDemo { String NAME = "成员变量固定是常量(即 static final 修饰的)" ; void method () ; default void method1 () { } private void method2 () { } static void method3 () { } }
实现接口
1 2 3 4 5 6 class InterfaceImplements implements InterfaceDemo , InterfaceDemo2 { @Override public void method () { } }
接口可以继承接口,且可以继承多个接口(extends 继承)
类可以实现多个接口
继承的方法与接口中有同名方法,先调用父类方法
内部类
类内再定义了个类,也没什么区别,但是实例化时要先实例化外部类
1 2 3 4 5 6 7 8 9 10 Outer.Inner in = new Outer ().new Inner (); Outer.Inner in = new Outer .Inner(); new Outer () { void method () { } }
内部类可以访问外部类的成员和方法
可以用外部类名指定访问外部类成员 (Outer.method()
)
静态内部类的访问类似与静态方法
枚举类
1 2 3 4 5 enum EnumDemo { a, b, c; }
简单来说,就是单例模式的拓展化,里面的常量其实就是枚举类的实例化,相当于固定了具体实例对象
泛型
简单来说,数据类型也是个变量,用 <E>
这样的方法定义个数据类型,可以手动传个类型,默认是 Object
一个更严格一点的约束方法 <E extends B>
那么数据类型 E 只能是 B 或者 B 的子类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Test <E> { public static <T> T method (T t) { return t; } public static void method (List<?> t) { } }
泛型只在编译阶段,编译好就没有了,称为泛型擦除
泛型不支持基本类型,要用引用数据类型
常用API
这里不具体解释方法,详细内容看文档有更详细的内容
传统时间类:Date, SimpleDateFormat, Calendar
JDK8新增的日期时间类:
LocalDate, localTime, LocalDateTime
ZoneId, ZoneDateTime
Instant (时间戳)
DateTimeFormatter
Duration, Period
Lambda表达式
主要是在排序函数中用到的,但不仅限于此,直接代码展示吧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 import java.util.Arrays;import java.util.Comparator;import java.util.Random;public class LambdaDemo { public static void main (String[] args) { final int n = 5 ; Random r = new Random (); People[] p = new People [n]; for (int i = 0 ; i < n; ++i) { p[i] = new People (r.nextInt(0 , 60 ), r.nextInt(150 , 200 ), r.nextInt(40 , 120 )); } System.out.println(Arrays.toString(p)); System.out.println("按年龄升序" ); Arrays.sort(p); System.out.println(Arrays.toString(p)); System.out.println("按升高升序" ); Arrays.sort(p, new Comparator <People>() { @Override public int compare (People o1, People o2) { return o1.height - o2.height; } }); System.out.println(Arrays.toString(p)); System.out.println("体重降序排序" ); Arrays.sort(p, ((o1, o2) -> o2.wight - o1.wight)); System.out.println(Arrays.toString(p)); System.out.println("体重升序排序" ); Arrays.sort(p, ComparePeople::compare); System.out.println(Arrays.toString(p)); ComparePeople cp = new ComparePeople (); System.out.println("年龄降序排序" ); Arrays.sort(p, cp::compare2); System.out.println(Arrays.toString(p)); } } class ComparePeople { public static int compare (People o1, People o2) { return o1.wight - o2.wight; } public int compare2 (People o1, People o2) { return o2.age - o1.age; } } class People implements Comparable <People> { int age, height, wight; public People (int age, int height) { this .age = age; this .height = height; } public People (int age, int height, int wight) { this .age = age; this .height = height; this .wight = wight; } public People () { } @Override public int compareTo (People o) { return this .age - o.age; } @Override public String toString () { return "People{" + "age=" + age + ", height=" + height + ", wight=" + wight + '}' ; } }
正则表达式
正则匹配规则参考官方文档,关键字:Pattern
使用正则匹配:String.matches()
Stream
流式处理数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import java.util.*;import java.util.stream.IntStream;import java.util.stream.Stream;public class StreamTest { public static void main (String[] args) { Random r = new Random (); List<Integer> list = new ArrayList <>(); int [] nums = new int [30 ]; for (int i = 0 , n = r.nextInt(20 , 50 ); i < n; ++i) { list.add(r.nextInt(100 )); } for (int i = 0 ; i < 30 ; ++i) { nums[i] = r.nextInt(100 ); } List<Integer> out = list.stream().filter(x -> x >= 50 ).distinct().sorted().map(x -> x - 50 ).toList(); System.out.println(out); IntStream sl = Arrays.stream(nums); sl.forEach(x -> System.out.print(x + ", " )); } }
异常处理
异常的基类是 java.lang.Throwable
,其子类有两个,一个
Error
,一个是 Exception
Error 可以不同管,Exception包含 RuntimeException
和其他异常(编译异常)
使用 try {} catch (e) {}
来捕获处理异常,也可以不出来,抛到上层调用处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class ExceptionTest { public static void main (String[] args) throws ArithmeticException { try { for (int i = 10 ; i >= 0 ; --i) { System.out.println(10 / i); } } catch (Exception e) { e.printStackTrace(); throw e; } finally { } } }
也可以自定义异常类
1 2 3 4 5 6 7 8 9 class ExampleRuntimeException extends RuntimeException { public ExampleRuntimeException () { } public ExampleRuntimeException (String message) { super (message); } }
文件读写
首先是文件或文件夹的路径的读写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import java.io.*;import java.util.*;public class FileTest { public static void main (String[] args) { File f = new File ("." ); System.out.println(f); if (f.isFile()) { System.out.println("这是一个文件" + f.getName()); } else if (f.isDirectory()) { System.out.println("这是一个路径" + f.getAbsolutePath()); } System.out.println( f.getAbsolutePath() + "路径下有 " + dfs(f) + " 个文件" ); } public static int dfs (File f) { File[] nxts = f.listFiles(); if (nxts == null ) return 0 ; int cnts = 0 ; for (File nxt : nxts) { if (nxt.isFile()) { System.out.println(nxt.getAbsolutePath()); ++cnts; } else { cnts += dfs(nxt); } } return cnts; } }
这是一个简单文件读取的示例,下面展示数据的编码和解码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import java.util.Arrays;public class EncodeDecode { public static void main (String[] args) throws Exception { String s = "这是一个字符串string" ; byte [] b = s.getBytes(); System.out.println(b.length); System.out.println(Arrays.toString(b)); byte [] b2 = s.getBytes("GBK" ); System.out.println(b2.length); System.out.println(Arrays.toString(b2)); String s1 = new String (b); System.out.println(s1); String s2 = new String (b, "GBK" ); System.out.println(s2); String s3 = new String (b2); System.out.println(s3); String s4 = new String (b2, "GBK" ); System.out.println(s4); } }
IO流
包含两种,一种是字节流,一种是字符流,分布是InputStream/OutputStream
和
Reader/writer
,不过这俩都是抽象类,常用的实现类是前面加个
File.
看看示例吧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 package com.szu.test;import java.io.*;public class IOStream { public static void main (String[] args) throws Exception { String path = "./Java-Study/src/text.txt" ; String opath = "./Java-Study/src/out.txt" ; InputStream is = new BufferedInputStream (new FileInputStream (path)); byte [] buffer = is.readAllBytes(); System.out.println(new String (buffer)); is.close(); OutputStream os = new BufferedOutputStream (new FileOutputStream (opath, true )); os.write(buffer); os.write("\r\n" .getBytes()); os.close(); System.out.println("===字符流===" ); try (Reader r = new FileReader (path)) { char [] rc = new char [1024 ]; int len; while ((len = r.read(rc)) != -1 ) { System.out.print(new String (rc, 0 , len)); } } catch (Exception e) { throw e; } try (Writer w = new FileWriter (opath, true )) { w.write("输出一些东西!\r\n" ); w.flush(); } catch (Exception e) { throw e; } try ( PrintWriter pw = new PrintWriter (new FileOutputStream (opath, true )) ) { pw.write("This is some words" ); pw.write(10086 ); pw.write('\n' ); } catch (Exception e) { throw e; } } }
多线程
线程创建和常用方法
看代码和注释吧~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 import java.util.Random;import java.util.concurrent.Callable;import java.util.concurrent.FutureTask;public class ThreadTest { public static void main (String[] args) throws Exception { Thread t1 = new PrintNumber (1 , "T1" ); Thread t2 = new PrintNumber (2 , "T2" ); t1.start(); t2.start(); t1.join(); t2.join(); Sound s1 = new Sound ("喵" ); Sound s2 = new Sound ("汪" ); Thread t3 = new Thread (s1); Thread t4 = new Thread (s2); t3.start(); t4.start(); Eat e1 = new Eat ("苹果" ); Eat e2 = new Eat ("香蕉" ); FutureTask<String> ft1 = new FutureTask <>(e1); FutureTask<String> ft2 = new FutureTask <>(e2); Thread t5 = new Thread (ft1); Thread t6 = new Thread (ft2); t5.start(); t6.start(); String r1 = ft1.get(); String r2 = ft2.get(); System.out.println(r1); System.out.println(r2); Thread tm = Thread.currentThread(); System.out.println("主线程名为: " + tm.getName()); } } class PrintNumber extends Thread { int val; public PrintNumber (int val, String name) { super (name); this .val = val; } public PrintNumber (int val) { this .val = val; } @Override public void run () { System.out.print(this .getName() + "开始执行\n" ); for (int i = val; i < 20 ; i += 2 ) { System.out.print(i + ", " ); } } } class Sound implements Runnable { public String s; public Sound (String s) { this .s = s; } @Override public void run () { for (int i = 0 ; i < 5 ; ++i) { System.out.print(s); } } } class Eat implements Callable <String> { private String food; public Eat (String food) { this .food = food; } @Override public String call () throws Exception { Random r = new Random (); int i = 0 , n = r.nextInt(2 , 6 ); for ( ;i < n; ++i) { System.out.println("吃 " + food); } return "吃了 " + n + " 个 " + food; } }
线程同步
同步代码块
同步代码块,将共享代码包装一下,需要传入一个对象作为锁(建议共享资源作为锁),实例对象建议使用当前对象作为锁,如果是静态方法建议使用
类名.class 作为锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public class SynchronizeTest { public static void main (String[] args) throws Exception { Number n1 = new Number (1 ); Thread t1 = new Thread (n1); Thread t2 = new Thread (n1); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(n1.getVal()); } } class Number implements Runnable { private int val; public Number (int val) { this .val = val; } public int getVal () { return val; } public void setVal (int val) { this .val = val; } @Override public void run () { synchronized (this ) { if (val > 0 ) { try { Thread.sleep(200 ); } catch (Exception e) { System.out.println("Exception" ); } val--; } } } }
同步方法
同步方法,即将整个方法加锁,内部隐含的其实就是前面同步代码块推荐的锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class Number implements Runnable { private int val; public Number (int val) { this .val = val; } public int getVal () { return val; } public void setVal (int val) { this .val = val; } @Override public synchronized void run () { if (val > 0 ) { try { Thread.sleep(200 ); } catch (Exception e) { System.out.println("Exception" ); } val--; } } }
Lock锁
自行定义一个锁对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;class Number implements Runnable { private int val; private final Lock lk = new ReentrantLock (); public Number (int val) { this .val = val; } public int getVal () { return val; } public void setVal (int val) { this .val = val; } @Override public void run () { lk.lock(); try { if (val > 0 ) { try { Thread.sleep(200 ); } catch (Exception e) { System.out.println("Exception" ); } val--; } } finally { lk.unlock(); } } }
线程间通信
主要是 wait
和 notify
,
notifyAll
三个 Object 自带的方法
注意上述三个方法必须要有锁的时候才能调用(即前面三种方法加锁)否则会报错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 public class ThreadCommunication { public static void main (String[] args) { Env e = new Env (); Thread p = new Thread (() -> { while (true ) e.produce(); }, "productor" ); Thread c = new Thread (() -> { while (true ) e.consume(); }, "consumer" ); p.start(); c.start(); } } class Env { private int n = 0 ; public Env () { } public synchronized void consume () { String cur = Thread.currentThread().getName(); try { if (n > 0 ) { --n; System.out.println(cur + " 消费~~~" + "(剩余 " + n + " 资源)" ); Thread.sleep(10 ); } this .notifyAll(); this .wait(); } catch (Exception e) { e.printStackTrace(); } } public synchronized void produce () { String cur = Thread.currentThread().getName(); try { if (n == 0 ) { ++n; System.out.println(cur + " 生产~~~" + "(剩余 " + n + " 资源)" ); Thread.sleep(10 ); } this .notifyAll(); this .wait(); } catch (Exception e) { e.printStackTrace(); } } }
线程池
有一堆线程,然后有个任务队列,依次从队列中取任务使用线程运行
说说参数吧
1 2 3 4 5 6 7 public ThreadPoolExecutor (int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
corePoolSize
: 核心线程数量,即长期存在的线程数量
maximumPoolSize
:
最大线程数量,要比核心线程数量多,多出的部分是临时线程,即只存在一段时间的线程
当核心线程占满、任务队列满了,才会开临时线程,新的的任务会直接到临时线程中运行(即不进队列而是越过队列中的任务直接运行)
如果还有任务来把临时线程也占满了,就会使用后面的拒绝策略
开了临时线程后会队列中的任务会按需分配到既有的空闲线程,不分核心和临时
keepAliveTime
: 临时线程存活时间
unit
: 存活时间单位,是个枚举类
workQueue
:
工作队列,一般由链表(无限大)和数组(优先大小)两种
threadFactory
: 生产线程的工程,暂时用默认的就好
handler
:
任务队列和临时进程都满了时候还有新任务来时使用的策略
下面是使用示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 import java.util.concurrent.*;public class ThreadPoolTest { public static void main (String[] args) throws Exception { ExecutorService es = new ThreadPoolExecutor (3 , 5 , 8 , TimeUnit.SECONDS, new ArrayBlockingQueue <>(4 ), Executors.defaultThreadFactory(), new ThreadPoolExecutor .CallerRunsPolicy()); Runnable target = new Sound ("喵" ); Runnable target2 = new Sound ("汪" ); Runnable target3 = new Sound ("啾" ); Runnable target4 = new Sound ("饿饿,饭饭" ); es.execute(target); es.execute(target); es.execute(target); es.execute(target2); es.execute(target2); es.execute(target2); es.execute(target2); es.execute(target3); es.execute(target3); es.execute(target4); Future<String> f1 = es.submit(new Eat ("橘子" )); Future<String> f2 = es.submit(new Eat ("葡萄" )); System.out.println(f1.get()); System.out.println(f2.get()); es.shutdown(); } }
网络编程
主要都在 java.net.*
包下。
基础架构:CS架构(客户端、服务端),BS架构(浏览器、服务端)
UDP: 无连接,不可靠
TCP: 有连接,可靠通信
UDP通信
客户端发送数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;public class Client { public static void main (String[] args) throws Exception { DatagramSocket socket = new DatagramSocket (); byte [] bytes = "一条来自客户端的数据!" .getBytes(); DatagramPacket packet = new DatagramPacket (bytes, bytes.length, InetAddress.getLocalHost(), 6666 ); socket.send(packet); System.out.println("客户端数据发生完毕!" ); socket.close(); } }
服务端接受数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import java.net.DatagramPacket;import java.net.DatagramSocket;public class Server { public static void main (String[] args) throws Exception { DatagramSocket socket = new DatagramSocket (6666 ); byte [] buffer = new byte [1024 * 64 ]; DatagramPacket packet = new DatagramPacket (buffer, buffer.length); socket.receive(packet); String rs = new String (buffer, 0 , packet.getLength()); System.out.println("收到了来自 " + packet.getAddress() + " (端口号为 " + packet.getPort() + ")发送的数据:" +rs); socket.close(); } }
TCP 通信
客户端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import java.io.DataOutputStream;import java.io.OutputStream;import java.net.Socket;import java.util.Scanner;public class Client { public static void main (String[] args) throws Exception { TCPClient(); } public static void TCPClient () throws Exception { Socket socket = new Socket ("127.0.0.1" , 8888 ); System.out.println("客户端已启动!" ); OutputStream os = socket.getOutputStream(); DataOutputStream dos = new DataOutputStream (os); Scanner sc = new Scanner (System.in); while (true ) { System.out.println("请输入要发送的消息:" ); String msg = sc.nextLine(); if ("exit" .equals(msg)) break ; dos.writeUTF(msg); } dos.close(); socket.close(); } }
服务端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 import java.io.DataInput;import java.io.DataInputStream;import java.io.InputStream;import java.net.ServerSocket;import java.net.Socket;public class Server { public static void main (String[] args) throws Exception { TCPServer(); } public static void TCPServer () throws Exception { ServerSocket ss = new ServerSocket (8888 ); System.out.println("服务端已启动,等待连接..." ); Socket socket = ss.accept(); InputStream is = socket.getInputStream(); DataInput dis = new DataInputStream (is); while (true ) { try { String rs = dis.readUTF(); System.out.println("收到来自 " + socket.getRemoteSocketAddress() + " 的消息:" + rs); } catch (Exception e) { System.out.println(socket.getRemoteSocketAddress() + " 已经退出,连接关闭!" ); break ; } } ss.close(); } }
使用多线程改造后的服务端,可以与多个客户端连接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 import java.io.DataInput;import java.io.DataInputStream;import java.io.InputStream;import java.net.ServerSocket;import java.net.Socket;public class Server { public static void main (String[] args) throws Exception { MultiTCPServer(); } public static void MultiTCPServer () throws Exception { ServerSocket ss = new ServerSocket (8888 ); System.out.println("服务端已启动,等待连接..." ); while (true ) { Socket socket = ss.accept(); System.out.println("收到" + socket.getRemoteSocketAddress() + " 连接,开始通信!" ); new ServerReadThread (socket).start(); } } } class ServerReadThread extends Thread { private Socket socket; public ServerReadThread (Socket socket) { this .socket = socket; } @Override public void run () { try { InputStream is = socket.getInputStream(); DataInput dis = new DataInputStream (is); while (true ) { try { String rs = dis.readUTF(); System.out.println("收到来自 " + socket.getRemoteSocketAddress() + " 的消息:" + rs); } catch (IOException e) { System.out.println(socket.getRemoteSocketAddress() + " 已经退出,连接关闭!" ); break ; } } } catch (Exception e) { throw new RuntimeException (e); } } }
反射
可以从字节码获取到类的信息,通常在框架中对类做一些通用操作用得多一些吗,具体实现可以看看代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.Arrays;public class TestClass { public static void main (String[] args) throws Exception { System.out.println("========== 反射第一步,获取 Class 对象 ==========" ); Class c1 = Student.class; System.out.println(c1.getName()); System.out.println(c1.getSimpleName()); Class c2 = Class.forName("com.szu.test.Reflect.Student" ); System.out.println(c1 == c2); Student s3 = new Student (); Class c3 = s3.getClass(); System.out.println(c3 == c1); System.out.println("========== 获取构造器 ==========" ); Constructor[] constructors = c1.getDeclaredConstructors(); for (Constructor constructor : constructors) { System.out.println(constructor.getName() + " -> " + constructor.getParameterCount() + " -> " + Arrays.toString(constructor.getParameterTypes())); } Constructor constructor = c1.getDeclaredConstructor(String.class, int .class); System.out.println(constructor.getName() + " -> " + constructor.getParameterCount() + " -> " + Arrays.toString(constructor.getParameterTypes())); System.out.println("========== 初始化对象 ==========" ); Student s = (Student) constructor.newInstance("YanLili" , 18 ); System.out.println(s); System.out.println("========== 获取成员变量 ==========" ); Field[] fields = c1.getDeclaredFields(); for (Field field : fields) { System.out.println(field.getName() + " -> " + field.getType()); } Field field = c1.getDeclaredField("age" ); field.setAccessible(true ); System.out.println(field.getName() + " -> " + field.getType() + " -> " + field.get(s)); field.set(s, 19 ); System.out.println(field.getName() + " -> " + field.getType() + " -> " + field.get(s)); System.out.println("========== 获取成员方法 ==========" ); Method[] methods = c1.getDeclaredMethods(); for (Method method : methods) { System.out.println(method.getName() + " -> " + Arrays.toString(method.getParameterTypes()) + " -> " + method.getReturnType()); } Method method = c1.getDeclaredMethod("getName" ); System.out.println(method.getName() + " -> " + Arrays.toString(method.getParameterTypes()) + " -> " + method.getReturnType()); System.out.println(method.invoke(s)); } }
注解
就是类似 @Override
这样的东西,自定义方法:
1 2 3 4 5 6 public @interface 注解名称 { public 属性类型 属性名() default 默认值; } @属性名称(属性名=值)
如果只有一个叫 value
的注解,使用可以不写属性名
注解本质上其实个继承了 Annotation
的接口,里面的数学都是抽象方法,使用时就是一个实现类对象
元注解
即注解的注解,主要有两个
@Target()
: 声明注解只能用哪里
@Retention()
:声明注解的保留周期
注解的功能,大概就是结合前面的反射做些通用性的事情,是一个标记