JavaSE 高级
一、面向对象(高级)
1、单例模式(Singleton)
单例模式(饿汉式):“立即加载”,随着类的加载,当前的唯一实例就创建了,是线程安全的。
package oopPlus.atguigu02.singleton;
/*
* 单例模式(饿汉式):“立即加载”,随着类的加载,当前的唯一实例就创建了
* */
public class BankTest {
public static void main(String[] args) {
Bank bank = Bank.getInstance();
}
}
class Bank {
//构造器私有化
private Bank() {
}
//类内部创建类的实例
private static Bank instance = new Bank();
//get方法获取当前类的实例
public static Bank getInstance() {
return instance;
}
}
单例模式(懒汉式):“延迟加载”,在需要使用的时候,进行创建,线程不安全。
package oopPlus.atguigu02.singleton;
/*
* 单例模式(懒汉式):“延迟加载”,在需要使用的时候,进行创建
* */
public class GirlFriendTest {
public static void main(String[] args) {
GirlFriend girlFriend = GirlFriend.getInstance();
}
}
class GirlFriend {
private GirlFriend() {
}
private static GirlFriend girlFriend = null;
public static GirlFriend getInstance() {
if (girlFriend == null) girlFriend = new GirlFriend();
return girlFriend;
}
}
2、代码块
1)静态代码块
-
随着类的加载而执行
-
由于类的加载只会执行一次,进而静态代码块的执行,也只会执行一次。
-
作用:初始化类的信息
-
内部可以声明变量、调用属性或方法、编写输出语句等操作。
-
静态代码块的执行要先于非静态代码块的执行。
-
如果声明有多个静态代码块,则按照声明的先后顺序执行。
-
静态代码块内部只能调用静态的结构(静态属性、方法),不能调用非静态的结构(非静态属性、方法)。
static {
...
}
2)非静态代码块
- 随着对象的创建而执行。
- 每当创建当前类的一个实例,就会执行一次非静态代码块。
- 作用:初始化对象的信息
- 内部可以声明变量、调用属性或方法、编写输出语句等操作。
- 如果声明有多个非静态代码块,则按照声明的先后顺序执行。
- 非静态代码块内部可以调用静态的结构(静态属性、方法),也可调用非静态的结构(非静态属性、方法)。
{
...
}
对象的实例变量可以赋值的位置及先后顺序
① 默认初始化
② 显式初始化或代码块中初始化
③ 构造器中初始化
④ 通过"对象.属性"或"对象.方法"的方式进行赋值
3、关键字 final
final 可以用来修饰的结构:类、方法、变量
-
final 修饰类:表示此类不能被继承。如:String、StringBuffer、StringBuilder。
-
final 修饰方法:表示此方法不能被重写。如:Object中的getClass()
-
final 修饰变量:可以修饰成员变量也能修饰局部变量,将其变为常量。
- 修饰成员变量:哪些位置可赋值?
- 显示赋值
- 代码块中赋值
- 构造器中赋值
- 修饰局部变量:一旦赋值不可修改
- 方法内声明的局部变量:在调用局部变量前一定要赋值。
- 方法的形参:在调用方法时,给形参赋值。
- 修饰成员变量:哪些位置可赋值?
-
final 和 static 搭配修饰的成员变量:全局常量。如:Math.PI
4、抽象类与抽象方法(abstract)
abstract 修饰类:
- 此类为抽象类,抽象类不能实例化。
- 抽象类中是包含构造器的,因为子类对象实例化时,需要直接或间接调用到父类的构造器。
- 抽象类中可以没有抽象方法,但抽象方法所在的类一定是抽象类。
abstract 修饰方法:
- 此方法为抽象方法,抽象方法只有方法的声明,没有方法体。
- 任何继承抽象类的子类都必须实现抽象类中的所有抽象方法,否则该子类也必须声明为抽象类。
- 抽象方法提供了一种多态性的机制,使得不同的子类可以以自己的方式实现相同的抽象方法。
5、模板方法设计模式(TemplateMethod)
public class TemplateMethodTest {
public static void main(String[] args) {
TemplateMethod t1 = new DrawMoney();
t1.process();
TemplateMethod t2 = new ManagerMoney();
t2.process();
}
}
abstract class TemplateMethod {
abstract void transact();
public void takeNumber() {
System.out.println("排队");
}
public void evaluate() {
System.out.println("反馈评价");
}
public final void process() {
this.takeNumber();
this.transact();
this.evaluate();
}
}
class DrawMoney extends TemplateMethod {
@Override
void transact() {
System.out.println("存钱!!!!!");
}
}
class ManagerMoney extends TemplateMethod {
@Override
void transact() {
System.out.println("理财!!!");
}
}
6、接口(interface)
接口的本质是契约、标准、规范
接口内部结构:
-
可以声明:
-
属性:用 public static final 修饰
public interface MyInterface { //public static final 可省略 public static final int MAX_VALUE = 100; }
-
方法:声明抽象方法,修饰为 public abstract
public interface MyInterface { //public abstract 可省略 public abstract void abstractMethod(); }
-
-
不可以声明:构造器、代码块。
接口与类的实现:class A extends SuperA implements B, C {}
-
类可以实现多个接口。
-
类针对于接口的多实现,一定程度上弥补了类的单继承的局限性。
-
类必须将实现的接口中的所有方法都重写,才能实例化。否则,此实现类必须声明为抽象类。
-
接口于接口之间可以多继承。
-
接口的多态性: 接口名 变量名 = new 实现类对象;
Flyable f = new Bullet(); f.fly();
接口和抽象类之间的对比:
JDK8 和 JDK9 的新特性
-
**静态方法(Static Methods):**从Java 8开始,接口还支持静态方法。静态方法以关键字
static
开头,可以通过接口名称直接调用,不需要实例化接口。public interface MyInterface { static void staticMethod() { System.out.println("Static method implementation"); } }
-
默认方法(Default Methods): 从Java 8开始,接口支持默认方法,即在接口中可以包含有具体实现的方法。默认方法以关键字
default
开头,并且可以在接口中提供默认的方法实现。public interface MyInterface { void abstractMethod(); default void defaultMethod() { System.out.println("Default method implementation"); } }
-
私有方法(Private Methods): 从Java 9开始,接口支持私有方法,这些方法只能在接口内部使用。私有方法以关键字
private
开头。public interface MyInterface { default void defaultMethod() { privateMethod(); } private void privateMethod() { System.out.println("Private method implementation"); } }
7、内部类
-
成员内部类(Member Inner Class): 成员内部类是定义在类内部的普通类。它与外部类的实例关联,可以访问外部类的成员,包括私有成员。
public class OuterClass { private int outerVar; public class InnerClass { public void innerMethod() { System.out.println("Accessing outerVar from innerMethod: " + outerVar); } } }
-
静态成员内部类(Static Nested Class): 静态成员内部类是定义在类内部的静态类。与成员内部类不同,它不依赖于外部类的实例,可以直接通过外部类名访问。
public class OuterClass { private static int outerStaticVar; public static class StaticInnerClass { public void staticInnerMethod() { System.out.println("Accessing outerStaticVar from staticInnerMethod: " + outerStaticVar); } } }
-
局部内部类(Local Inner Class): 局部内部类是定义在方法内部的类,它只在该方法内可见。通常用于解决某个特定问题。
public class OuterClass { public void outerMethod() { class LocalInnerClass { public void localInnerMethod() { System.out.println("Inside localInnerMethod"); } } LocalInnerClass localInner = new LocalInnerClass(); localInner.localInnerMethod(); } }
-
匿名内部类(Anonymous Inner Class): 匿名内部类是没有显式定义类名的内部类,通常用于实现接口或继承抽象类。它可以在创建对象的同时提供实现。
public interface MyInterface { void myMethod(); } public class OuterClass { public MyInterface createAnonymousClass() { return new MyInterface() { @Override public void myMethod() { System.out.println("Anonymous class implementation"); } }; } }
8、枚举类 Enum
JDK 5.0 之前自定义枚举类的方式(了解):
public class SeasonTest {
public static void main(String[] args) {
System.out.println(Season.SPRING);
System.out.println(Season.SUMMER.getSeasonName());
System.out.println(Season.SUMMER.getSeasonDesc());
}
}
// jdk5.0前定义枚举类方式
class Season {
//1.声明当前类的对象的实例变量,使用private final 修饰
private final String seasonName;
private final String seasonDesc;
//2.私有化类的构造器
private Season(String seasonName, String seasonDesc) {
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
//3.提供实例变量的get方法
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
//4.创建当前类的实例对象,需要用public static final修饰
public static final Season SPRING = new Season("春天", "春暖花开");
public static final Season SUMMER = new Season("夏天", "夏日炎炎");
public static final Season AUTUMN = new Season("秋天", "秋高气爽");
public static final Season WINTER = new Season("冬天", "白雪皑皑");
@Override
public String toString() {
return "Season{" +
"seasonName='" + seasonName + '\'' +
", seasonDesc='" + seasonDesc + '\'' +
'}';
}
}
JDK 5.0 可使用 enum 定义枚举类
public enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
}
枚举类实现单例模式
package oopPlus.atguigu07._enum;
// 使用enum定义枚举类
public enum Season {
// 利用枚举值实现单例模式
SPRING("春天", "春暖花开"),
SUMMER("夏天", "夏日炎炎"),
AUTUMN("秋天", "秋高气爽"),
WINTER("冬天", "白雪皑皑");
// 私有化实例变量
private final String seasonName;
private final String seasonDesc;
// 私有化构造方法
private Season(String seasonName, String seasonDesc) {
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
// 提供实例变量的get方法
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
@Override
public String toString() {
return "Season{" +
"seasonName='" + seasonName + '\'' +
", seasonDesc='" + seasonDesc + '\'' +
'}';
}
}
// 测试类
class SeasonTest {
public static void main(String[] args) {
System.out.println(Season.SPRING);
System.out.println(Season.SUMMER.getSeasonName());
System.out.println(Season.SUMMER.getSeasonDesc());
}
}
9、注解 Annotation
注解的应用场景
- 生成文档相关的注解
- 在编译时进行格式检查(三个基本注解)
- 跟踪代码依赖性,实现替代配置文件功能
三个基本常用注解
@Override
:限定重写父类方法,该注解只能用于方法@Deprecated
:用于表示所修饰的元素已过时。通常是因为所修饰的结构危险或有更好选择@SuppressWarnings
:抑制编译器警告
**元注解:**对现有的注解进行解释说明的注解。例如4个元注解
- @Target:用于描述注解的适用范围。可以通过枚举类型ElementType的10个常量对象来指定TYPE,METHOD,CONSTRUCTOR,PACKAGE …
- @Retention:用于描述注解的生命周期。可通过枚举类型RetentionPolicy的3个常量对象来指定 SOURCE(源代码),CLASS(字节码),RUNTIME(运行时)
- @Documented:表面这个注解应该被 javadoc 工具记录
- @Inherited:允许子类继承父类中的注解
10、包装类 Wrapper
包装类(Wrapper Class)是一种将基本数据类型包装成对象的类,Java 提供了一组包装类来实现这种功能。这些包装类位于 java.lang
包中,分别对应于基本数据类型 boolean
、byte
、short
、int
、long
、float
、double
和 char
。
装箱和拆箱: 装箱是将基本数据类型转换为对应的包装类对象,而拆箱是将包装类对象转换为基本数据类型。例如:
// 装箱
Integer intValue = Integer.valueOf(10);
// 拆箱
int intValueUnboxed = intValue.intValue();
自动装箱和拆箱: 从 Java 5 开始,引入了自动装箱和拆箱的特性,允许直接将基本数据类型赋值给对应的包装类对象,或者将包装类对象赋值给对应的基本数据类型,而无需显式调用装箱和拆箱方法。
// 自动装箱
Integer intValue = 10;
// 自动拆箱
int intValueUnboxed = intValue;
String 与基本数据类型之间的转换
String 转换为基本数据类型:
-
通过包装类的 parseXxx 方法: 每个基本数据类型的包装类都提供了
parseXxx
方法,用于将字符串转换为对应的基本数据类型。String str = "123"; int intValue = Integer.parseInt(str); double doubleValue = Double.parseDouble(str);
-
通过包装类的 valueOf 方法: 类似于
parseXxx
方法,valueOf
方法也可以用于将字符串转换为对应的包装类对象。String str = "123"; Integer intValue = Integer.valueOf(str); Double doubleValue = Double.valueOf(str);
基本数据类型转换为 String:
-
通过 String 类的 valueOf 方法:
String
类的valueOf
方法可以将基本数据类型转换为字符串。int intValue = 123; double doubleValue = 45.67; String strInt = String.valueOf(intValue); String strDouble = String.valueOf(doubleValue);
-
通过 String.format 方法: 使用
String.format
方法进行格式化,也可以将基本数据类型转换为字符串。int intValue = 123; double doubleValue = 45.67; String strInt = String.format("%d", intValue); String strDouble = String.format("%.2f", doubleValue);
11、IDEA 常用快捷键
(1)通用型
(2)提高编写速度
(3)类结构、查找和查看源码
(4)查找、替换与关闭
(5)调整格式
二、异常处理
1、异常概述
2、Java 异常体系
1)Throwable
java.lang.Throwable
类是Java程序执行过程中发生的异常事件对应的类的根父类。
常用方法:
public void printStackTrace()
:打印异常的详细信息。public String getMessage()
:获取发生异常的原因。
2)Error 和 Exception
3、编译时异常与运行时异常
常见的编译时异常
- ClassNotFoundException
- FileNotFoundException
- IOException
常见的运行时异常
- ArrayIndexOutOfBoundsException
- NullPointerException
- ClassCastException
- NumberFormatException
- InputMismatchException
- ArithmeticException
4、异常处理方式
使用 try-catch-finally 块可以捕获可能抛出的异常,并在 catch 块中处理异常。这种方式适用于需要针对不同类型的异常采取不同处理逻辑的情况。使用 finally 块可以确保资源在代码执行完毕后被正确释放,不管是否发生异常。这种方式适用于需要进行资源清理的情况。
try {
// 可能抛出异常的代码块
// ...
} catch (ExceptionType1 e1) {
// 处理 ExceptionType1 异常的代码
} catch (ExceptionType2 e2) {
// 处理 ExceptionType2 异常的代码
} finally {
// finally 块,无论是否发生异常都会执行
// 通常用于资源释放等操作
}
使用 throws 关键字声明方法可能抛出的异常,由调用该方法的代码负责处理异常。这种方式适用于将异常传递给调用方处理的情况。
public void myMethod() throws MyException {
// 可能抛出 MyException 异常的方法体
// ...
}
使用 throw 关键字手动抛出异常,可以根据特定条件抛出异常。这种方式适用于需要自定义异常信息或在特定条件下抛出异常的情况。
public void myMethod(int value) {
if (value < 0) {
throw new IllegalArgumentException("Value cannot be negative");
}
// 其他代码
}
5、自定义异常类
在 Java 中,你可以通过创建自定义异常类来表示特定的错误或异常情况,以便在程序中抛出和捕获这些异常。自定义异常类通常是继承自 Exception
类或其子类,或者继承自 RuntimeException
类或其子类。
-
创建自定义异常类:
public class MyException extends Exception { // 无参构造方法 public MyException() { super(); } // 带有详细信息的构造方法 public MyException(String message) { super(message); } // 带有详细信息和原因的构造方法 public MyException(String message, Throwable cause) { super(message, cause); } // 带有原因的构造方法 public MyException(Throwable cause) { super(cause); } }
-
在需要抛出异常的地方使用自定义异常类:
public void myMethod() throws MyException { // 检查条件,如果不满足则抛出自定义异常 if (conditionNotMet) { throw new MyException("Custom error message"); } // 其他代码 }
-
在调用处捕获并处理自定义异常:
try { myMethod(); } catch (MyException e) { // 处理自定义异常 System.out.println("An error occurred: " + e.getMessage()); }
三、多线程
1、程序、进程与线程
2、创建和启动线程
public class EvenNumberTest {
public static void main(String[] args) {
PrintNumber p1 = new PrintNumber();
p1.start();
PrintNumber p2 = new PrintNumber();
p2.start();
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0) System.out.println(Thread.currentThread().getName() + i);
}
}
}
class PrintNumber extends Thread {
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0) System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
方式2:实现 Runnable 接口
① 创建一个实现Runnable接口的类
② 实现接口中的run() – 将此线程要执行的操作,声明在此方法体中
③ 创建当前实现类的对象
④ 将此对象作为参数传递到Thread类的构造器中,创建Thread类的实例
⑤ Thread类的实例调用start() : 1.启动线程 2.调用当前线程的run()
public class EvenNumberTest {
public static void main(String[] args) {
EvenNumberPrint p1 = new EvenNumberPrint();
Thread t1 = new Thread(p1);
t1.start();
//匿名实现类的匿名对象
new Thread(new Runnable() {
//奇数
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
if (i % 2 != 0) System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}).start();
}
}
class EvenNumberPrint implements Runnable {
@Override
public void run() {
//偶数
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0) System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
3、Thread类的常用方法和生命周期
1)构造方法
- public Thread():分配一个新的线程对象。
- public Thread(String name):分配一个指定名字的新的线程对象。
- public Thread(Runnable target):指定创建线程的目标对象,它实现了Runnable接口的run方法。
- public Thread(Runnable target, String name):分配一个带有指定目标新的线程对象并指定名字。
2)常用方法
- start():① 启动线程 ②调用线程的run()
- run():将线程要执行的操作,声明在run()中
- currentThread():获取当前执行代码对应的线程
- getName():获取线程名
- setName():设置线程名
- sleep(long millis):静态方法,调用时,可以使得当前线程睡眠指定的毫秒数
- yield():静态方法,一旦执行该方法,就释放CUP的执行权
- join():在线程A中通过线程B调用 join() ,意味着A进入阻塞态,知道B执行结束,A才结束阻塞态,继续执行。
3)线程的优先级
-
getPriority():获取线程的优先级
-
setPriority():设置线程的优先级 ([1,10])
Thread类的内部声明的三个常量:
① MAX_PRIORITY(10):最高优先级
② MIN_PRIORITY(1):最低优先级
③ NORM_PRIORITY(5):普通优先级,默认main线程是普通优先级
4)生命周期
4、线程安全
Runnable 方式
public class WindowTest {
public static void main(String[] args) {
Window w = new Window();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.start();
t2.start();
t3.start();
}
}
class Window implements Runnable {
int ticket = 100;
@Override
public void run() {
while (true) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (this) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + ":" + ticket);
ticket--;
} else {
break;
}
}
}
}
}
Thread 方式
public class WindowTest {
public static void main(String[] args) {
Window t1 = new Window();
Window t2 = new Window();
Window t3 = new Window();
t1.start();
t2.start();
t3.start();
}
}
class Window extends Thread {
static int ticket = 100;
@Override
public void run() {
while (true) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (Window.class) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + ":" + ticket);
ticket--;
} else {
break;
}
}
}
}
}
Runnable 方式
public class WindowTest {
public static void main(String[] args) {
Window w = new Window();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.start();
t2.start();
t3.start();
}
}
class Window implements Runnable {
int ticket = 100;
boolean isFlag = true;
@Override
public void run() {
while (isFlag) {
show();
}
}
public synchronized void show() { //同步监视器是:this。即为 w ,是唯一的
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + ":" + ticket);
ticket--;
} else {
isFlag = false;
}
}
}
Thread 方式
public class WindowTest {
public static void main(String[] args) {
Window w1 = new Window();
Window w2 = new Window();
Window w3 = new Window();
w1.start();
w2.start();
w3.start();
}
}
class Window extends Thread {
static int ticket = 100;
static boolean isFlag = true;
@Override
public void run() {
while (isFlag) {
show();
}
}
//public synchronized void show() { 同步监视器默认是:this。即为 w1,w2,w3 不唯一,线程不安全
public static synchronized void show() { //此时同步监视器为当前类本身, 即为 Window.class,是唯一的
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + ":" + ticket);
ticket--;
} else {
isFlag = false;
}
}
}
5、死锁
6、Lock 锁
package Thread.at02.threadSafe._Lock;
import java.util.concurrent.locks.ReentrantLock;
public class WindowTest {
public static void main(String[] args) {
Window t1 = new Window();
Window t2 = new Window();
Window t3 = new Window();
t1.start();
t2.start();
t3.start();
}
}
class Window extends Thread {
static int ticket = 100;
//1.创建Lock的实例,需要确保多个线程共用一个Lock实例,故考虑将此对象声明为 static final
private static final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
//2.执行lock()方法,锁定对共享资源的调用
lock.lock();
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + ":" + ticket);
ticket--;
} else {
break;
}
} finally {
//3.unlock()的调用,释放对共享数据的锁定
lock.unlock();
}
}
}
}
7、线程的通信
面试题
生产者与消费者案例
public class ProducerConsumerTest {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producer producer = new Producer(clerk);
Consumer consumer = new Consumer(clerk);
producer.setName("生产者");
consumer.setName("消费者");
producer.start();
consumer.start();
}
}
class Clerk {
private int produceNum = 0;
public synchronized void addProduce() {
if (produceNum >= 20) {
try {
wait(); // 缓冲区满,阻塞
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
produceNum++;
System.out.println(Thread.currentThread().getName() + "生产了第" + produceNum + "个产品");
notify(); // 唤醒消费者线程
}
}
public synchronized void minusProduce() {
if (produceNum <= 0) {
try {
wait(); // 缓冲区空,阻塞
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
System.out.println(Thread.currentThread().getName() + "消费了第" + produceNum + "个产品");
produceNum--;
notify(); // 唤醒生产者线程
}
}
}
//生产者
class Producer extends Thread {
private Clerk clerk;
public Producer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println("生产者开始生产");
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//生产产品
clerk.addProduce();
}
}
}
//消费者
class Consumer extends Thread {
private Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println("消费者开始消费");
while (true) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//消费产品
clerk.minusProduce();
}
}
}
四、常用类与基础API
1、String 类
1)String 的理解与不可变性
2)String 的实例化与连接操作
3)构造器与常用方法
2、StringBuffer 和 StringBuilder
StringBuffer: 可变的字符序列,是线程安全的,所有的方法都被 synchronized 关键字修饰,因此适合在多线程环境中使用。
StringBuilder: 可变的字符序列,不是线程安全的,因此在单线程环境中使用它更高效,性能更好。
扩容机制:
- 初始化容量: 当创建一个新的
StringBuffer
或StringBuilder
对象时,会初始化一个初始容量。默认情况下,初始容量为 16 个字符。 - 扩容条件: 当添加的字符数超过了当前容量时,就需要进行扩容。扩容的条件通常是当前容量不足以容纳要添加的字符。
- 增长策略: 在扩容时,
StringBuffer
和StringBuilder
会根据其增长策略来确定新容量的大小。默认扩容为原有容量的 2 倍 + 2 。 - 复制内容: 扩容时,会创建一个新的字符数组,并将原来的字符数组中的内容复制到新数组中。这样做是为了保留之前添加的字符,并确保在新的容量下仍然可以继续添加字符。
如果大体确定要操作的字符个数,建议使用带 int capacity 参数的构造器,可以避免底层多次扩容。
常用API
3、日期时间API
1)JDK8 之前的API
Date类常用方法:
getTime()
:- 返回自1970年1月1日00:00:00以来的毫秒数。
setTime(long time)
:- 设置
Date
对象的时间,参数为自1970年1月1日00:00:00以来的毫秒数。
- 设置
getYear()
:- 获取年份(从1900年开始)。
getMonth()
:- 获取月份(从0开始,0表示一月,11表示十二月)。
getDate()
:- 获取日期(月份中的第几天)。
getHours()
、getMinutes()
、getSeconds()
:- 获取小时、分钟、秒。
setYear(int year)
、setMonth(int month)
、setDate(int date)
:- 设置年、月、日。
Calendar类常用方法:
getInstance()
:- 获取一个默认时区和语言环境的
Calendar
对象实例。
- 获取一个默认时区和语言环境的
set(int year, int month, int date)
:- 设置年、月、日。
setTime(Date date)
:- 设置
Calendar
对象的时间,参数为Date
对象。
- 设置
get(int field)
:- 获取指定字段的值,如
Calendar.YEAR
、Calendar.MONTH
、Calendar.DATE
等。
- 获取指定字段的值,如
add(int field, int amount)
:- 对指定字段进行加减操作,如加一年、减一个月等。
getTime()
:- 将
Calendar
对象转换为Date
对象。
- 将
getTimeInMillis()
:- 获取自1970年1月1日00:00:00以来的毫秒数。
setTimeZone(TimeZone zone)
:- 设置时区。
2)JDK8 中的API
本地日期时间:LocalDate、LocalTime、LocalDateTime
4、Comparable 和 Comparator
1)使用 Comparable 接口实现自然排序
@Override
public int compareTo(Object o) {
if (o == this) {
return 0;
}
if (o instanceof Product) {
Product p = (Product) o;
return Double.compare(this.price, p.price);
}
throw new RuntimeException("类型不匹配");
}
2)使用 Comparator 接口实现定制排序
@Test
public void test() {
String[] arr = new String[]{"Tom", "Jerry", "Tony", "Rose", "Jack", "Lucy"};
Arrays.sort(arr, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof String && o2 instanceof String) {
String s1 = (String) o1;
String s2 = (String) o2;
return -s1.compareTo(s2);
}
throw new RuntimeException("类型不匹配");
}
})
}
五、集合框架
1、Collection 常用方法
2、迭代器 Iterator
3、List
4、Set
5、Map
@Test
public void test() {
HashMap hashMap = new HashMap();
hashMap.put("AA", 56);
hashMap.put(1, 22);
hashMap.put("BB", 33);
hashMap.put(new Person("Tom", 32), 23);
System.out.println(hashMap);
Set keySet = hashMap.keySet();
System.out.println(keySet);
Collection values = hashMap.values();
System.out.println(values);
Set entrySet = hashMap.entrySet();
Iterator iterator = entrySet.iterator();
while(iterator.hasNext()) {
Map.Entry entry = (Map.Entry) iterator.next();
System.out.println(entry.getKey() + "-->" + entry.getValue());
}
}
@Test //定制排序
public void test() {
Comparator comparator = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if (o1 == o2) return 0;
if (o1 instanceof Person && o2 instanceof Person) {
Person p1 = (Person) o1;
Person p2 = (Person) o2;
int flag = p1.getName().compareTo(p2.getName());
if (flag != 0) return flag;
return p1.getAge() - p2.getAge();
}
throw new RuntimeException("类型不匹配");
}
};
TreeMap treeMap = new TreeMap(comparator);
Person p1 = new Person("Tom", 1);
Person p2 = new Person("Yan", 21);
Person p3 = new Person("Dom", 22);
Person p4 = new Person("Jerry", 18);
Person p5 = new Person("LiHua", 25);
treeMap.put(p1, 13);
treeMap.put(p2, 36);
treeMap.put(p3, 77);
treeMap.put(p4, 13);
treeMap.put(p5, 5);
Set entrySet = treeMap.entrySet();
for (Object entry: entrySet) {
System.out.println(entry);
}
}
6、Collections 工具类
六、泛型
1、泛型的使用
package Generic.use;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.function.Predicate;
public class Exer {
public static void main(String[] args) {
// 1、创建ArrayList集合对象,指定泛型为<Integer>
ArrayList<Integer> arrayList = new ArrayList<>();
// 2、添加5个[0, 100)的随机整数
for (int i = 0; i < 5; i++) {
int random = (int) (Math.random() * 100);
arrayList.add(random);
}
// 3、foreach遍历
for (int i: arrayList) {
System.out.println(i);
}
// 4、用集合的removeIf删除偶数,为Predicate接口指定泛型<Integer>
arrayList.removeIf(new Predicate<Integer>() {
@Override
public boolean test(Integer value) {
if (value % 2 == 0) return true;
return false;
}
});
System.out.println("--------");
// 5、使用Iterator迭代器输出剩下元素,为Iterator接口指定泛型<Integer>
Iterator<Integer> iterator = arrayList.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
2、自定义泛型
1)自定义泛型类
自定义泛型是在编写Java代码时定义参数化类型的一种方式,它允许你编写更加灵活、可重用的代码,同时提高了代码的类型安全性。通过使用泛型,你可以编写一次代码,以不同类型的参数进行多次使用。
public class MyGenericClass<T> {
private T data;
public MyGenericClass(T data) {
this.data = data;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public void printData() {
System.out.println("Data: " + data);
}
}
在上面的示例中,MyGenericClass
是一个泛型类,它有一个类型参数T
。在实例化这个类时,你可以指定具体的类型作为参数,比如Integer
、String
等。例如:
MyGenericClass<Integer> intObj = new MyGenericClass<>(10);
System.out.println(intObj.getData()); // 输出:10
MyGenericClass<String> stringObj = new MyGenericClass<>("Hello");
System.out.println(stringObj.getData()); // 输出:Hello
2)自定义泛型方法
除了泛型类之外,Java还支持泛型方法。你可以在普通类、接口、抽象类中定义泛型方法,允许这些方法在被调用时接受不同类型的参数。
public class MyUtils {
public static <T> T findMax(T[] array) {
if (array == null || array.length == 0) {
return null;
}
T max = array[0];
for (T item : array) {
if (item.compareTo(max) > 0) { // 这里假设T是Comparable类型
max = item;
}
}
return max;
}
}
在这个示例中,findMax
方法是一个泛型方法,它可以接受任意类型的数组作为参数,并且返回该数组中的最大值。你可以将不同类型的数组传递给这个方法,并得到相应类型的最大值。
Integer[] intArray = {3, 6, 2, 8, 1};
System.out.println(MyUtils.findMax(intArray)); // 输出:8
String[] stringArray = {"apple", "banana", "orange"};
System.out.println(MyUtils.findMax(stringArray)); // 输出:orange
3、通配符 ‘?’
七、数据结构和集合源码
1、数据结构
2、ArrayList 源码
扩容机制
3、Vector 源码
扩容机制
4、LinkedList 源码
底层使用双向链表
添加一个元素
5、HashMap
1)JDK7
2)JDK8
对于:
HashMap<String, Integer> hashMap = new HashMap<>();
hashMap.put(“AA”, 11);
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // 所有其他字段默认
}
其中:
static final float DEFAULT_LOAD_FACTOR = 0.75f;
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);//哈希值1 —> 哈希值2
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
// ...略...
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
扩容
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
// ...略...
return newTab;
}
Node
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
// ...略...
}
6、LinkedHashMap
7、HashSet和LinkedHashSet
HashSet(底层是HashMap)
LinkedHashSet(底层是LinkedHashMap)
八、File类与IO流
1、File 类
1)概述
2)构造方法
在IDEA中,如果使用单元测试方法,相对于当前的 module 来讲;
如果使用 main() 方法,相对于当前的 project 来讲
3)常用方法
2、IO 流原理及分类
1)IO 流原理
2)流的分类
3)API
3、FileReader 和 FileWriter 读写数据
1)FileReader 读数据
package File.file;
import org.junit.Test;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
//读取hello.txt的内容
public class FileReaderTest {
@Test
public void test1() throws IOException {
//1.创建File类的对象,对于文件
File file = new File("hello.txt");
//2.创建输入型字符流,用于读数据
FileReader fr = new FileReader(file);
//3.读取数据
int data = fr.read();
while (data != -1) {
System.out.print((char) data);
data = fr.read();
}
//4.流资源的关闭
fr.close();
}
//try-catch-finally 确保资源关闭
@Test
public void test2() {
FileReader fr = null;
try {
//1.创建File类的对象,对于文件
File file = new File("hello.txt");
//2.创建输入型字符流,用于读数据
fr = new FileReader(file);
//3.读取数据
int data = fr.read();
while (data != -1) {
System.out.print((char) data);
data = fr.read();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.流资源的关闭
try {
if (fr != null) {
fr.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//优化,每次读取多个字符放入数组中,减少与磁盘的交互
@Test
public void test3() {
FileReader fr = null;
try {
//1.创建File类的对象,对于文件
File file = new File("hello.txt");
//2.创建输入型字符流,用于读数据
fr = new FileReader(file);
//3.读取数据
char[] cbuffer = new char[5];
int len = fr.read(cbuffer);
while (len != -1) {
for(char c: cbuffer) {
System.out.print(c);
}
len = fr.read(cbuffer);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.流资源的关闭
try {
if (fr != null) {
fr.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2)FileWriter 写数据
package File.file;
import org.junit.Test;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
//将内存中数据写出到指定文件中
public class FileWriterTest {
@Test
public void test1() {
FileWriter fw = null;
try {
//1.创建File类对象,指明要写出的文件名称
File file = new File("info.txt");
//2.创建输出流
fw = new FileWriter(file); //覆盖的构造器
//3.写出的具体过程
fw.write("hahaha\n");
fw.write("咯咯123\n");
System.out.println("输出成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭资源
try {
if (fw != null)
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3)读写数据
package File.file;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
//复制一份hello.txt文件,命名为hello_copy.txt
public class FileReaderWriterTest {
public static void main(String[] args) {
FileReader fr = null;
FileWriter fw = null;
try {
//1.创建File类的对象
File srcFile = new File("hello.txt");
File destFile = new File("hello_copy.txt");
//2.创建输入流、输出流
fr = new FileReader(srcFile);
fw = new FileWriter(destFile);
//3.数据的读入和写出
char[] cbuffer = new char[5];
int len;
while ((len = fr.read(cbuffer)) != -1) {
fw.write(cbuffer, 0, len);
}
System.out.println("复制成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭流资源
try {
if (fw != null)
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fr != null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
4、FileInputStream 和 FileOutputStream
package File.file;
import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
//复制一份image.jpg,命名为image_copy.jpg
public class FileStreamTest {
@Test
public void test1() {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
//1.创建相关的File类的对象
File file1 = new File("image.jpg");
File file2 = new File("image_copy.txt");
//2.创建相关的字节流
fis = new FileInputStream(file1);
fos = new FileOutputStream(file2);
//3.数据的读入和写出
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
System.out.println("复制成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭资源
try {
if (fis != null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fos != null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
5、处理流1—缓冲流
1)BufferedInputStream 和 BufferedOutputStream
package File.buffer;
import org.junit.Test;
import java.io.*;
public class BufferedStream {
@Test
public void test1() {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//1.创建相关的File类的对象
File file1 = new File("image.jpg");
File file2 = new File("image_copy.jpg");
//2.创建相关的字节流、缓冲流
FileInputStream fis = new FileInputStream(file1);
FileOutputStream fos = new FileOutputStream(file2);
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//3.数据的读入和写出
byte[] buffer = new byte[50];
int len;
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
System.out.println("复制成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭资源
try {
if (bis != null)
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (bos != null)
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2)BufferedReader 和 BufferedWriter
package File.buffer;
import org.junit.Test;
import java.io.*;
public class BufferedReaderWriterTest {
@Test
public void test1() {
BufferedReader br = null;
try {
File file = new File("hello.txt");
br = new BufferedReader(new FileReader(file));
char[] cBuffer = new char[1024];
int len;
while ((len = br.read(cBuffer)) != -1) {
String str = new String(cBuffer, 0, len);
System.out.println(str);
}
/* (readLine(): 每次读取一行文本的数据,返回的字符不包括换行符)
*
* String data;
* while((data = br.readLine()) != null) {
* System.out.println(data + "\n");
* }
*/
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Test
public void test2() {
BufferedReader br = null;
BufferedWriter bw = null;
try {
//1.创建相关的File类的对象
File file1 = new File("hello.txt");
File file2 = new File("hello_copy.txt");
//2.创建相关的字符流、缓冲流
br = new BufferedReader(new FileReader(file1));
bw = new BufferedWriter(new FileWriter(file2));
//3.数据的读入和写出
String data;
while((data = br.readLine()) != null) {
bw.write(data);
bw.newLine(); //表示换行操作
bw.flush(); //刷新的方法,调用时主动将内存中的数据写到磁盘文件中
}
System.out.println("复制成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭资源
try {
if (br != null)
br.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (bw != null)
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
6、处理流2—转换流
-
InputStreamReader
-
OutputStreamWriter
package File.IOStream;
import java.io.*;
public class IOStreamRW {
public static void main(String[] args) {
// 定义文件对象
File file1 = new File("input.txt");
File file2 = new File("output.txt");
// 定义输入流和输出流变量,用于后续的资源管理
InputStreamReader isr = null; // 声明字符输入流变量
OutputStreamWriter osw = null; // 声明字符输出流变量
try {
// 创建文件输入流,将字节流转换为字符流,并指定字符集为UTF-8
FileInputStream fis = new FileInputStream(file1);
isr = new InputStreamReader(fis, "UTF-8");
// 创建文件输出流,指明内存中字符存储到文件中的字节过程使用的编码集为GBK
FileOutputStream fos = new FileOutputStream(file2);
osw = new OutputStreamWriter(fos, "GBK");
// 读写过程
char[] cBuffer = new char[1024]; // 创建一个字符数组,用于暂存读取到的数据
int len; // 用于记录每次读取到的字符数
while((len = isr.read(cBuffer)) != -1) { // 循环读取数据,直到读取到文件末尾
osw.write(cBuffer, 0, len); // 将读取到的数据写入到输出流中
}
System.out.println("操作完成");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (osw != null)
osw.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (isr != null)
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
7、处理流3—对象流
package File.IOStream;
import org.junit.Test;
import java.io.*;
public class ObjectInputOutputStreamTest {
//序列化过程:使用ObjectOutputStream流实现。将内存中的Java对象保存在文件中或通过网络传输出去
@Test
public void test1() throws IOException {
File file = new File("object.txt");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
//写出数据即为序列化过程
oos.writeUTF("江山如此多娇,引无数英雄竞折腰");
oos.flush();
oos.writeObject("仰天长笑出门去,我辈岂是蓬蒿人");
oos.close();
}
//反序列化过程:使用ObjectInputStream流实现。将文件中的数据或网络传输过来的数据还原为内存中的Java对象
@Test
public void test2() throws IOException, ClassNotFoundException {
File file = new File("object.txt");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
//读取文件中的对象 (反序列化的过程)
String str1 = ois.readUTF();
System.out.println(str1);
String str2 = (String) ois.readObject();
System.out.println(str2);
ois.close();
}
}
九、网络编程
1、网络传输三要素
- IP 地址
- 端口号
- 网络通信协议
2、InetAddress
InetAddress 类的一个实例就代表一个具体的ip地址。
实例化方法:
- InetAddress getByName(String host):获取指定 ip 对应的 InetAddress 的实例。
- InetAddress getLocalHost():获取本地 ip 对应的 InetAddress 的实例。
常用方法:
- getHostName():获取对应的域名
- getHostAddress():获取对应 ip 地址
3、TCP 与 UDP 协议
1)Socket
2)TCP 网络编程
package Socket.TCP;
import org.junit.Test;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
//客户端发送内容给服务端,服务端进行打印
public class TCPTest1 {
//客户端
@Test
public void client() {
Socket socket = null;
OutputStream os = null;
try {
//1.创建一个Socket
InetAddress inetAddress = InetAddress.getByName("192.168.241.1");
int port = 8989; //对方的端口号
socket = new Socket(inetAddress, port);//指明对方的IP地址和端口号
//2.发送数据
os = socket.getOutputStream();
os.write("你好,我是客户端".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.关闭socket、流
try {
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (os != null) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//服务器端
@Test
public void server() {
ServerSocket serverSocket = null;
Socket socket = null;
InputStream is = null;
ByteArrayOutputStream baos = null;
try {
//1.创建一个ServerSocket
int port = 8989; //自己的端口号
serverSocket = new ServerSocket(port);
//2.调用accept(),接收客户端的Socket
System.out.println("服务器端已开启......");
socket = serverSocket.accept();
System.out.println("收到了来自于" + socket.getInetAddress().getHostAddress() + "的连接");
//3.接收数据
is = socket.getInputStream();
byte[] buffer = new byte[5];
int len;
baos = new ByteArrayOutputStream();
while ((len = is.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
System.out.println(baos);
System.out.println("数据接收完毕");
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭资源
try {
if (baos != null) {
baos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (serverSocket != null) {
serverSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("客户端已关闭......");
}
}
3)UDP 网络编程
package Socket.UDP;
import org.junit.Test;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDPTest {
//发送端
@Test
public void sender() throws IOException {
//1.创建DatagramSocket实例
DatagramSocket ds = new DatagramSocket();
//2.将数据,目的ip,目的端口号封装在DatagramPacket数据报中
InetAddress inetAddress = InetAddress.getByName("192.168.241.1");
int port = 9090;
byte[] bytes = "我是发送端".getBytes("utf-8");
DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, inetAddress, port);
//3.发送数据
ds.send(packet);
ds.close();
}
//接收端
@Test
public void receiver() throws IOException {
//1.创建DatagramSocket实例
int port = 9090;
DatagramSocket ds = new DatagramSocket(port);
//2.创建数据报的对象,用于接收发送端发送的数据
byte[] buffer = new byte[1024 * 64];
DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
//3.接收数据
ds.receive(packet);
//4.获取数据,打印到控制台
String str = new String(packet.getData(), 0, packet.getLength());
System.out.println(str);
ds.close();
}
}
十、反射机制
1、反射的概念
2、反射和Class的理解
package reflection;
import org.junit.Test;
//获取Class实例的几种方法
public class ClassTest {
@Test
public void test1() throws ClassNotFoundException {
//1.调用运行时类的静态属性: class
Class clazz1 = Person.class;
System.out.println(clazz1);
//2.调用运行时类的对象的getClass()
Person p1 = new Person();
Class clazz2 = p1.getClass();
System.out.println(clazz2);
//3.调用Class的静态方法forName(String className)
String className = "reflection.Person"; //全类名
Class clazz3 = Class.forName(className);
System.out.println(clazz3);
}
}