网络编程

计算机网络:是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。

网络编程:就是用来实现网络互连的不同计算机上运行的程序间可以进行数据交换。

UDP传输

  • 发送Send
    1. 创建DatagramSocket, 随机端口号
    2. 创建DatagramPacket, 指定数据, 长度, 地址, 端口
    3. 使用DatagramSocket发送DatagramPacket
    4. 关闭DatagramSocket
  • 接收Receive
    1. 创建DatagramSocket, 指定端口号
    2. 创建DatagramPacket, 指定数组, 长度
    3. 使用DatagramSocket接收DatagramPacket
    4. 关闭DatagramSocket
    5. 从DatagramPacket中获取数据
  • 接收方获取ip和端口号
    • String ip = packet.getAddress().getHostAddress();
    • int port = packet.getPort();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// send
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;

public class Demo1_Send {
public static void main(String[] args) throws Exception {
String str = "what are you 弄啥呢?";
DatagramSocket socket = new DatagramSocket(); //创建Socket相当于创建码头
//创建Packet相当于集装箱
DatagramPacket packet = new DatagramPacket(str.getBytes(), str.getBytes().length, InetAddress.getByName("127.0.0.1"), 6666);
socket.send(packet); //发货,将数据发出去
socket.close(); //关闭码头
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// receive
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class Demo1_Receive {
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket(6666); //创建Socket相当于创建码头
DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);//创建Packet相当于创建集装箱
socket.receive(packet); //接货,接收数据

byte[] arr = packet.getData(); //获取数据
int len = packet.getLength(); //获取有效的字节个数
System.out.println(new String(arr,0,len));
socket.close();
}
}

UDP传输优化

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
// send
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Scanner;

public class Demo2_Send {
public static void main(String[] args) throws Exception {
Scanner sc = new Scanner(System.in); //创建键盘录入对象
DatagramSocket socket = new DatagramSocket(); //创建Socket相当于创建码头

while(true) {
String line = sc.nextLine(); //获取键盘录入的字符串
if("quit".equals(line)) {
break;
}
//创建Packet相当于集装箱
DatagramPacket packet = new DatagramPacket(line.getBytes(), line.getBytes().length, InetAddress.getByName("127.0.0.1"), 6666);
socket.send(packet); //发货,将数据发出去
}
socket.close(); //关闭码头
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// receive
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class Demo2_Receive {
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket(6666); //创建Socket相当于创建码头
DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);//创建Packet相当于创建集装箱

while(true) {
socket.receive(packet); //接货,接收数据

byte[] arr = packet.getData(); //获取数据
int len = packet.getLength(); //获取有效的字节个数
String ip = packet.getAddress().getHostAddress();//获取ip地址
int port = packet.getPort(); //获取端口号
System.out.println(ip + ":" + port + ":" + new String(arr,0,len));
}
}
}

UDP传输多线程

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
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Scanner;

public class Demo3_MoreThread {
public static void main(String[] args) {
new Receive().start();
new Send().start();
}
}

class Receive extends Thread {
public void run() {
try {
DatagramSocket socket = new DatagramSocket(6666); //创建Socket相当于创建码头
DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);//创建Packet相当于创建集装箱

while(true) {
socket.receive(packet); //接货,接收数据

byte[] arr = packet.getData(); //获取数据
int len = packet.getLength(); //获取有效的字节个数
String ip = packet.getAddress().getHostAddress(); //获取ip地址
int port = packet.getPort(); //获取端口号
System.out.println(ip + ":" + port + ":" + new String(arr,0,len));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

class Send extends Thread {
public void run() {
try {
Scanner sc = new Scanner(System.in); //创建键盘录入对象
DatagramSocket socket = new DatagramSocket(); //创建Socket相当于创建码头

while(true) {
String line = sc.nextLine(); //获取键盘录入的字符串
if("quit".equals(line)) {
break;
}
//创建Packet相当于集装箱
DatagramPacket packet = new DatagramPacket(line.getBytes(), line.getBytes().length, InetAddress.getByName("127.0.0.1"), 6666);
socket.send(packet); //发货,将数据发出去
}
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

TCP协议

  • 客户端
    1. 创建Socket连接服务端(指定ip地址,端口号)通过ip地址找对应的服务器
    2. 调用Socket的getInputStream()和getOutputStream()方法获取和服务端相连的IO流
    3. 输入流可以读取服务端输出流写出的数据
    4. 输出流可以写出数据到服务端的输入流
  • 服务端
    1. 创建ServerSocket(需要指定端口号)
    2. 调用ServerSocket的accept()方法接收一个客户端请求,得到一个Socket
    3. 调用Socket的getInputStream()和getOutputStream()方法获取和客户端相连的IO流
    4. 输入流可以读取客户端输出流写出的数据
    5. 输出流可以写出数据到客户端的输入流
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Client
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class Demo1_Client {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket socket = new Socket("127.0.0.1", 12345);

InputStream is = socket.getInputStream(); //获取客户端输入流
OutputStream os = socket.getOutputStream(); //获取客户端的输出流

byte[] arr = new byte[1024];
int len = is.read(arr); //读取服务器发过来的数据
System.out.println(new String(arr,0,len)); //将数据转换成字符串并打印

os.write("学习挖掘机哪家强?".getBytes()); //客户端向服务器写数据

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
// Server
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Demo1_Server {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(12345);

Socket socket = server.accept(); //接受客户端的请求
InputStream is = socket.getInputStream(); //获取客户端输入流
OutputStream os = socket.getOutputStream(); //获取客户端的输出流

os.write("百度一下你就知道".getBytes()); //服务器向客户端写出数据

byte[] arr = new byte[1024];
int len = is.read(arr); //读取客户端发过来的数据
System.out.println(new String(arr,0,len)); //将数据转换成字符串并打印

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
// Client
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class Demo2_Client {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket socket = new Socket("127.0.0.1", 12345);
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); //将字节流包装成了字符流
PrintStream ps = new PrintStream(socket.getOutputStream()); //PrintStream中有写出换行的方法

System.out.println(br.readLine());
ps.println("我想报名黑马程序员");
System.out.println(br.readLine());
ps.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
23
24
25
26
27
28
29
30
31
32
33
34
35
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Demo2_Server {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(12345);

while(true) {
final Socket socket = server.accept(); //接受客户端的请求
new Thread() {
public void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); //将字节流包装成了字符流
PrintStream ps = new PrintStream(socket.getOutputStream()); //PrintStream中有写出换行的方法

ps.println("欢迎咨询黑马程序员");
System.out.println(br.readLine());
ps.println("不好意思,爆满了");
System.out.println(br.readLine());
socket.close();
} catch (IOException e) {

e.printStackTrace();
}
}
}.start();
}
}
}

反射

类的加载概述和加载时机

类的加载概述

  • 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载连接初始化三步来实现对这个类进行初始化。

  • 加载

    就是指将class文件读入内存,并为之创建一个Class对象。任何类被使用时系统都会建立一个Class对象。

    我们通过Javac程序创建.class文件,这个.class文件存储在硬盘中,然后我们把这个.class文件加载到内存里。

  • 连接

    • 验证 : 是否有正确的内部结构,并和其他类协调一致
    • 准备 : 负责为类的静态成员分配内存,并设置默认初始化值
    • 解析 : 将类的二进制数据中的符号引用替换为直接引用
  • 初始化 : 就是我们以前讲过的初始化步骤

加载时机

  • 创建类的实例
  • 访问类的静态变量,或者为静态变量赋值
  • 调用类的静态方法
  • 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
  • 初始化某个类的子类
  • 直接使用java.exe命令来运行某个主类

类加载器的概述和分类

类加载器的概述:负责将.class文件加载到内存中,并为之生成对应的Class对象

类加载器的分类

  • Bootstrap ClassLoader : 根类加载器
  • Extension ClassLoader : 扩展类加载器
  • Sysetm ClassLoader : 系统类加载器

类加载器的作用

  • Bootstrap ClassLoader : 根类加载器
    • 也被称为引导类加载器,负责Java核心类的加载
    • 比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
  • Extension ClassLoader 扩展类加载器
    • 负责JRE的扩展目录中jar包的加载。
    • 在JDK中JRE的lib目录下ext目录
  • Sysetm ClassLoader 系统类加载器
    • 负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径

反射

反射概述

  • JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
  • 对于任意一个对象,都能够调用它的任意一个方法和属性;
  • 这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
  • 要想解剖一个类,必须先要获取到该类的字节码文件对象。
  • 而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象。

三种方式

  • Object类的getClass()方法,判断两个对象是否是同一个字节码文件
  • 静态属性class,锁对象
  • Class类中静态方法forName(),读取配置文件

1570425753840

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import com.heima.bean.Person;

public class Demo1_Reflect {
public static void main(String[] args) throws ClassNotFoundException {
// 方式一
Class clazz1 = Class.forName("com.heima.bean.Person");
// 方式二
Class clazz2 = Person.class;
// 方式三
Person p = new Person();
Class clazz3 = p.getClass();

System.out.println(clazz1 == clazz2); // true
System.out.println(clazz2 == clazz3); // true
}
}

Class.forName()读取配置文件

使用多态

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
public class Demo2_Reflect {
public static void main(String[] args) throws Exception {
Juicer j = new Juicer();
// 不使用反射 , 使用多态
j.run(new Apple());
j.run(new Orange());
}
}

interface Fruit {
public void squeeze();
}
class Apple implements Fruit {
public void squeeze() {
System.out.println("榨出一杯苹果汁儿");
}
}

class Orange implements Fruit {
public void squeeze() {
System.out.println("榨出一杯橘子汁儿");
}
}

class Juicer {
public void run(Fruit f) {
f.squeeze();
}
}

使用反射

1
2
3
4
5
6
7
8
9
10
11
public class Demo2_Reflect {
public static void main(String[] args) throws Exception {
Juicer j = new Juicer(); //创建榨汁机
// 反射获取字节码文件
BufferedReader br = new BufferedReader(new FileReader("config.properties"));
Class clazz = Class.forName(br.readLine()); //获取该类的字节码文件

Fruit f = (Fruit) clazz.newInstance(); //创建实例对象
j.run(f);
}
}

这样我们不必要修改代码 , 直接修改配置文件即可

通过反射获取带参构造方法 :Constructor

Constructor

Class类的newInstance()方法是使用该类无参的构造函数创建对象, 如果一个类没有无参的构造函数, 就不能这样创建了,可以调用Class类的getConstructor(String.class,int.class)方法获取一个指定的构造函数 , 然后再调用Constructor类的newInstance("张三",20)方法创建对象

  • getConstructor(String.class,int.class) : 获取有参构造
  • newInstance("张三",20) : 创建对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.lang.reflect.Constructor;
import com.heima.bean.Person;

public class Demo3_Constructor {
public static void main(String[] args) throws Exception {
// 反射获取字节码文件
Class clazz = Class.forName("com.heima.bean.Person");
//getConstructor:获取有参构造
Constructor c = clazz.getConstructor(String.class,int.class);
//newInstance:通过有参构造创建对象
Person p = (Person) c.newInstance("张三",23);
System.out.println(p);
}
}

通过反射获取成员变量 : Field

  • getField(String) : 获取公共属性
  • getDeclaedField("name") : 获取私有属性
  • set(obj, "李四") : 设置属性
  • setAccessible(true) : 设置访问权限(如果是私有的需要先设置访问权限)
  • get(obj) : 获取指定对象中该字段的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

import com.heima.bean.Person;

public class Demo4_Field {
public static void main(String[] args) throws Exception {
Class clazz = Class.forName("com.heima.bean.Person");
Constructor c = clazz.getConstructor(String.class,int.class); //获取有参构造
Person p = (Person) c.newInstance("张三",23); //通过有参构造创建对象

Field f = clazz.getDeclaredField("name"); //暴力反射获取字段
f.setAccessible(true); //去除私有权限
f.set(p, "李四");

System.out.println(p);
}
}

封装成方法 :

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
import java.lang.reflect.Field;

public class Test3 {
public static void main(String[] args) throws Exception {
Student s = new Student("张三", 23);
System.out.println(s);

Tool t = new Tool();
t.setProperty(s, "name", "李四");
System.out.println(s);
}
}

public class Tool {
//此方法可将obj对象中名为propertyName的属性的值设置为value。
public void setProperty(Object obj, String propertyName, Object value) throws Exception {
Class clazz = obj.getClass(); //获取字节码对象
Field f = clazz.getDeclaredField(propertyName); //暴力反射获取字段
f.setAccessible(true); //去除权限
f.set(obj, value);
}
}

class Student {
private String name;
private int age;
public Student() {
super();

}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}

通过反射获取方法 : Method

  • Class.getMethod(String, Class...) : 获取类中公有方法
  • Class.getDeclaredMethod(String, Class...) : 获取类中的私有方法
  • invoke(Object, Object...) : 调用已经获取的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import com.heima.bean.Person;

public class Demo5_Method {
public static void main(String[] args) throws Exception {
Class clazz = Class.forName("com.heima.bean.Person");
Constructor c = clazz.getConstructor(String.class,int.class); //获取有参构造
Person p = (Person) c.newInstance("张三",23); //通过有参构造创建对象

Method m = clazz.getMethod("eat"); //获取eat方法
m.invoke(p); // 调用获取的m方法

Method m2 = clazz.getMethod("eat", int.class); //获取有参的eat方法
m2.invoke(p, 10);
}
}

封装为方法 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;

public class Test2 {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new FileReader("xxx.properties")); //创建输入流关联xxx.properties
Class clazz = Class.forName(br.readLine()); //读取配置文件中类名,获取字节码对象

DemoClass dc = (DemoClass) clazz.newInstance(); //通过字节码对象创建对象
dc.run();
}
}

public class DemoClass {
public void run() {
System.out.println("welcome to heima!");
}
}

通过反射越过泛型检查

泛型只在编译期有效,在运行期会被擦除掉

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.lang.reflect.Method;
import java.util.ArrayList;

public class student2 {
public static void main(String[] args) throws Exception {
ArrayList<Integer> list = new ArrayList<>();
list.add(111);
list.add(222);

Class clazz = Class.forName("java.util.ArrayList"); //获取字节码对象
Method m = clazz.getMethod("add", Object.class); //获取add方法
m.invoke(list, "abc");

System.out.println(list); // [111,222,abc]
}
}

动态代理

  • 概述:本来应该自己做的事情,请了别人来做,被请的人就是代理对象。

    举例:春节回家买票让人代买

  • 动态代理其实就是通过反射来生成一个代理

  • 在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理。我们有更强大的代理cglib,Proxy类中的方法创建动态代理类对象

  • public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

  • 最终会调用InvocationHandler的方法

  • InvocationHandler Object invoke(Object proxy,Method method,Object[] args)

  • 示例 :

    1. User类要实现add()delete()两个方法.

    2. 执行这两个方法时都需要进行权限校验日志记录

    3. 一般写法 :

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      public class UserImp implements User {

      @Override
      public void add() {
      System.out.println("权限校验");
      System.out.println("添加功能");
      System.out.println("日志记录");
      }

      @Override
      public void delete() {
      System.out.println("权限校验");
      System.out.println("删除功能");
      System.out.println("日志记录");
      }
      }
    4. 上面写法缺陷就是 :

      • 权限校验和日志记录其实和add()delete()方法并没有什么关系 .
      • 于是想将这两个方法独立出去
      • 在python中一般使用装饰器 , 在Java中可以使用动态代理
    5. 动态代理:

      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.lang.reflect.InvocationHandler;
      import java.lang.reflect.Method;
      import java.lang.reflect.Proxy;

      public class Test {
      public static void main(String[] args) {
      // 没有实现 权限校验 和 日志记录 功能
      UserImp ui = new UserImp();
      ui.add();
      ui.delete();
      /* 输出:
      添加功能
      删除功能
      */


      // 实现 权限校验 和 日志记录 功能
      MyInvocationHandler m = new MyInvocationHandler(ui);
      User u = (User)Proxy.newProxyInstance(ui.getClass().getClassLoader(), ui.getClass().getInterfaces(), m);
      u.add();
      u.delete();
      /* 输出:
      权限校验
      添加功能
      日志记录
      权限校验
      删除功能
      日志记录
      */
      }
      }


      public class MyInvocationHandler implements InvocationHandler {
      private Object target;

      public MyInvocationHandler(Object target) {
      this.target = target;
      }

      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      System.out.println("权限校验");
      method.invoke(target, args); //执行被代理target对象的方法
      System.out.println("日志记录");
      return null;
      }
      }


      public class UserImp implements User {
      @Override
      public void add() {
      System.out.println("添加功能");
      }

      @Override
      public void delete() {
      System.out.println("删除功能");
      }
      }

模版(Template)设计模式

  • 模版设计模式概述

    模版方法模式就是定义一个算法的骨架,而将具体的算法延迟到子类中来实现

  • B:优点和缺点

    • 优点

      使用模版方法模式,在定义算法骨架的同时,可以很灵活的实现具体的算法,满足用户灵活多变的需求

    • 缺点

      如果算法骨架有修改的话,则需要修改抽象类

    • 步骤

      1. 装饰
      2. 单例
      3. 简单工厂
      4. 工厂方法
      5. 适配器
      6. 模版
    • 示例 :

      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 class Demo1_Template {
      public static void main(String[] args) {
      Demo d = new Demo();
      System.out.println(d.getTime());
      }

      }

      abstract class GetTime {
      // fianl :防止方法被重写
      public final long getTime() {
      long start = System.currentTimeMillis();
      code();
      long end = System.currentTimeMillis();
      return end - start;
      }
      // 这部分代码交由子类实现
      // 抽象方法保证该方法必须被重写
      public abstract void code();
      }

      class Demo extends GetTime {
      @Override
      public void code() {
      int i = 0;
      while(i < 100000) {
      System.out.println("x");
      i++;
      }
      }
      }

枚举

自定义枚举类

  • 示例一 : 空参构造

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public class Demo1_Enum {
    public static void main(String[] args) {
    Week mon = Week.MON;
    Week tue = Week.TUE;
    Week wed = Week.WED;

    System.out.println(mon);
    }
    }


    public class Week {
    public static final Week MON = new Week();
    public static final Week TUE = new Week();
    public static final Week WED = new Week();

    private Week(){} //私有构造,不让其他类创建本类对象
    }
  • 示例二 : 有参构造

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public class Demo1_Enum {
    public static void main(String[] args) {
    Week2 mon = Week2.MON;
    System.out.println(mon.getName());
    }
    }


    public class Week {
    private String name;

    public static final Week MON = new Week("星期一");
    public static final Week TUE = new Week("星期二");
    public static final Week WED = new Week("星期三");

    //私有有参构造
    private Week(String name){
    this.name = name;
    }

    public String getName(){
    return name;
    }
    }
  • 示例三 : 匿名类内部方法

    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
    public class Demo1_Enum {
    public static void main(String[] args) {
    Week3 mon = Week3.MON;
    mon.show();
    }
    }

    public abstract class Week3 {
    private String name;

    // 使用匿名类
    public static final Week3 MON = new Week3("星期一") {
    public void show() {
    System.out.println("星期一");
    }
    };
    public static final Week3 TUE = new Week3("星期二"){
    public void show() {
    System.out.println("星期二");
    }
    };
    public static final Week3 WED = new Week3("星期三"){
    public void show() {
    System.out.println("星期三");
    }
    };

    //私有构造,不让其他类创建本类对象
    private Week3(String name){
    this.name = name;
    }
    public String getName() {
    return name;
    }

    public abstract void show();
    }

通过enum实现枚举类

  • 定义枚举类要用关键字enum
  • 所有枚举类都是Enum的子类
  • 枚举类的第一行上必须是枚举项,最后一个枚举项后的分号是可以省略的,但是如果枚举类有其他的东西,这个分号就不能省略。建议不要省略
  • 枚举类可以有构造器,但必须是private的,它默认的也是private的。
  • 枚举类也可以有抽象方法,但是枚举项必须重写该方法
  • 枚举在switch语句中的使用

示例一 : 空参构造

1
2
3
4
5
6
7
8
9
10
11
public class Demo1_Enum {
public static void main(String[] args) {
Week mon = Week.MON;
System.out.println(mon); // MON
}
}

public enum Week {
// 第一行上必须是枚举项 , 分号建议不要省略
MON,TUE,WED;
}

示例二 : 有参构造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Demo1_Enum {
public static void main(String[] args) {
Week2 mon = Week2.MON;
System.out.println(mon.getName()); // 星期一
}
}

public enum Week2 {
MON("星期一"),TUE("星期二"),WED("星期三");

private String name;
// 构造器必须是private
private Week2(String name) {
this.name = name;
}
public String getName() {
return name;
}

public String toString() {
return name;
}
}

示例三 : 匿名类内部方法

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
public class Demo1_Enum {
public static void main(String[] args) {
Week3 mon = Week3.MON;
mon.show(); // 星期一
}
}


public enum Week3 {
MON("星期一"){
public void show() {
System.out.println("星期一");
}
},TUE("星期二"){
public void show() {
System.out.println("星期二");
}
},WED("星期三"){
public void show() {
System.out.println("星期三");
}
};

private String name;
private Week3(String name) {
this.name = name;
}
public String getName() {
return name;
}

public abstract void show();
}

示例四 : 使用switch

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
public class Demo1_Enum {
public static void main(String[] args) {
Week3 mon = Week3.TUE;
switch (mon) {
case MON:
System.out.println("星期一");
break;
case TUE:
System.out.println("星期二");
break;
}
}
}

public enum Week3 {
MON("星期一"){
public void show() {
System.out.println("星期一");
}
},TUE("星期二"){
public void show() {
System.out.println("星期二");
}
},WED("星期三"){
public void show() {
System.out.println("星期三");
}
};

private String name;
private Week3(String name) {
this.name = name;
}
public String getName() {
return name;
}

public abstract void show();
}

枚举类的常见方法

  • int ordinal()
  • int compareTo(E o)
  • String name()
  • String toString()
  • <T> T valueOf(Class<T> type,String name)
  • values()
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
public class Demo2_Enum {
public static void main(String[] args) {
Week2 mon = Week2.MON;
Week2 tue = Week2.TUE;
Week2 wed = Week2.WED;

//枚举项都是有编号的
System.out.println(mon.ordinal()); // 0
System.out.println(tue.ordinal()); // 1
System.out.println(wed.ordinal()); // 2

//比较的是编号
System.out.println(mon.compareTo(tue)); // -1
System.out.println(mon.compareTo(wed)); // -2

//获取实例名称
System.out.println(mon.name()); // MON
//调用重写之后的toString方法
System.out.println(mon.toString()); // 星期一
}
}

public enum Week2 {
MON("星期一"),TUE("星期二"),WED("星期三");

private String name;
private Week2(String name) {
this.name = name;
}
public String getName() {
return name;
}

public String toString() {
return name;
}
}
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
public class Demo2_Enum {
public static void main(String[] args) {
//通过字节码对象获取枚举项
Week2 mon = Week2.valueOf(Week2.class, "MON");
System.out.println(mon); // 星期一

Week2[] arr = Week2.values(); // Week2.values():["星期一","星期二","星期三"]
for (Week2 week2 : arr) {
System.out.println(week2);
}
}
}

public enum Week2 {
MON("星期一"),TUE("星期二"),WED("星期三");

private String name;
private Week2(String name) {
this.name = name;
}
public String getName() {
return name;
}

public String toString() {
return name;
}
}

JDK7新特性

  • 二进制字面量
  • 数字字面量可以出现下划线
  • switch 语句可以用字符串
  • 泛型简化,菱形泛型
  • 异常的多个catch合并,每个异常用或|
  • try-with-resources 语句,1.7版标准的异常处理代码
1
2
3
4
5
6
public class Demo1_JDK7 {
public static void main(String[] args) {
System.out.println(0b110); // 6
System.out.println(100_000); // 100_000等同于100000
}
}

JDK8的新特性

接口中可以定义有方法体的方法,

  • 如果是非静态,必须用default修饰
  • 如果是静态的就不用了
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
public class student3 {
public static void main(String[] args) {
Demo d = new Demo();
d.print(); // hello world

Inter.method(); // static method

Demo d = new Demo();
d.run(); // 10
}
}

// 接口中可以定义有方法体的方法
interface Inter {
// 非静态方法必须有default关键字
public default void print() {
System.out.println("hello world");
}
// 静态方法不需要使用default关键字
public static void method() {
System.out.println("static method");
}
}

class Demo implements Inter {
public void run() {
// JDK8,前面的final可以省略
int num = 10;
class Inner {
public void func() {
System.out.println(num);
}
}

Inner i = new Inner();
i.fun();
}
}

局部内部类在访问他所在方法中的局部变量必须用final修饰,为什么?
因为当调用这个方法时,局部变量如果没有用final修饰,他的生命周期和方法的生命周期是一样的,当方法弹栈,这个局部变量也会消失,那么如果局部内部类对象还没有马上消失想用这个局部变量,就没有了,如果用final修饰会在类加载的时候进入常量池,即使方法弹栈,常量池的常量还在,也可以继续使用