第三部分 高级篇
第9章 常用类解析
9.1 Object类
Object是所有类的父类,如果一个类没有继承一个类,那么它默认继承Object类。
类 Object 是类层次结构的根类。每个类都使用 Object 作为超类。
Object类的hashCode()方法
public int hashCode();
返回该对象的哈希码值。默认情况下,该方法会根据对象的地址来计算。(每个对象都有一个特有的hashCode值)
Object类的getClass()方法
public final Class getClass()
返回此 Object 的运行时的类。
public String getName()
可以通过 Class 类中的一个方法,获取对象的真实类的全名称。
Object类的toString()方法
public String toString()
返回该对象的字符串表示,一般要重写
==号和equals方法的区别
- ”==“
- “==” 是一个比较运算符号,既可以比较基本数据类型,也可以比较引用数据类型,
- 基本数据类型比较的是值,引用数据类型比较的是地址值
- equals()
- equals() 方法是一个方法,只能比较引用数据类型,
- 所有的对象都会继承 Object 类中的方法,
- 没有重写 Object 类中的 equals 方法时,equals方法和
==
号比较引用数据类型无区别,重写后的equals方法比较的是对象中的属性。(Object的equals和==
无异)
9.2 StringBuffer类、StringBuilder类
StringBuffer类
- 线程安全的可变字符序列。synchronized 同步锁 线程安全—适合多线程
- 可变字符序列,一个类似于 String 的字符串缓冲区
- StringBuffer append(XXX) 往末尾添加
- StringBuffer insert(int offset, String str) 指定位置添加
- StringBuffer reverse() 反转
- String toString()转化成字符串
StringBuilder类
- 可变的字符序列。此类提供一个与 StringBuffer 兼容的 API,
- 但不保证同步 ,非线程安全—适合单线程
String 类
- 其值在创建之后不能更改,可以共享 本质 是char value[]
- String str=“abc”; 内存的方法区有一个字符串常量池,先去判断常量池中是有"abc"
如果没有,就创建一个,放在常量池中,把地址给str,如果有则把地址给变量空间 - 如果对字符串频繁修改就会频繁创建字符串到常量池,占用大量空间,期间还会产生大量垃圾,这时就可以选择可变字符序列的StringBuffer和StringBuilder
9.3 基本数据类型包装类
-
基本数据类型(8)
数值型(6):byte、short、int、long、float、double
字符型:char
布尔型:boolean
基本数据类型都是存储栈中,栈中存储的就是值本身,赋值、参数传递的时候操作的也是值本身。
Java是面向对象的,对象都有属性和方法,基本数据类型只有值,没有方法,不遵循面向对象。Java为了完善,为了提供更多的方法去操作基本数据数据就提供出来了包装类。 -
包装类:
byte— Byte
short—Short
int – Integer
long-- Long
float --Float
double-- Double
char --Character
boolean-- Boolean
注意:除了int–Integer、char–Character,其它的都是首字母变大小 -
包装类的继承关系
- Object
- Number(abstract父类)
Byte、Short、Integer、Long、Float、Double - Character
- Boolean
- Number(abstract父类)
- 使用ValueOf()创建Integer时,如果参数在-128~127之间,就从缓存中返回对象,否则创建新对象
- Object
-
总结:
1. Byte、Short、Integer、Long的valueOf方法都有缓存,【-128,127】
2. Float、Double的valueOf方法没有缓存,每次都会新创建对象
3. Character 的valueOf方法 有缓存,【0,127】
4. Boolean 的valueOf方法 有缓存,有静态的常量true,false,直接返回 -
装箱:基本数据类型—>包装类型 自动转换
-
拆箱:包装类型–基本类型 自动转换
第10章 异常
- 什么是异常?
在程序编译或运行过程中出现的不正常的情况 - 三种类型的异常
- 检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
- 运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
- 错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。
10.1 处理异常
- 方式一:添加判断 if…else
- 缺点:程序员会把精力放在避免异常,无法集中在业务上了。一直在不漏洞,但是也不一定能补全业务代码被处理异常的代码淹没了,可读性不强
- 方式二:异常处理机制: 预置一些异常处理程序—如果异常情况—执行相应的处理程序,这样就不会让程序中断
捕获异常
使用 try 和 catch 关键字可以捕获异常。try/catch 代码块放在异常可能发生的地方。
try/catch代码块中的代码称为保护代码,使用 try/catch 的语法如下:
try
{
// 程序代码
}catch(ExceptionName e1)
{
//Catch 块
}
当保护代码块中发生一个异常时,try 后面的 catch 块就会被检查。
如果发生的异常包含在 catch 块中,异常会被传递到该 catch 块,这和传递一个参数到方法是一样。
- 正常情况:执行try,继续后续代码
- 异常情况:出现异常之后的代码不再执行,转到catch块执行,继续后续代码出现异常,系统会产生一个异常对象,会和catch块中捕获的类型匹配,匹配上才执行其中的代码,匹配不上就报错
多重捕获块
一个 try 代码块后面跟随多个 catch 代码块的情况就叫多重捕获。
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}catch(异常类型3 异常的变量名3){
// 程序代码
}
多种异常的话,我们可以使用多个catch语句来捕获。从上往下匹配,上面的匹配上,下面的就不再匹配,所以,catch种捕获的异常类型应该从小到大来写,否则会报错,因为有些catch块永远也执行不了
finally块
try
{
// 程序代码
}catch(ExceptionName e1)
{
//Catch 块
}
finally
{
//程序代码
}
finally不管是否出现异常,都会去执行的代码
当有return语句的时候,先执行return前的语句,再执行finally,最后return (return 无法阻拦)
阻拦finally执行
System.exit(status),直接停掉JVM ,status如果是0,表示正常退出。非0,表示异常退出。
如果之前try/catch中已经return了然后执行 finally语句 finally语句里面也有有return 那么执行哪个return?最终是finally中的return起了作用
10.2 抛出异常
throws/throw 关键字:
如果一个方法没有捕获到一个检查性异常,那么该方法必须使用 throws 关键字来声明。
-
throws 关键字放在方法签名的尾部。
-
可以使用 throw 关键字抛出一个异常,无论它是新实例化的还是刚捕获到的。
import java.io.*;
public class className
{
public void deposit(double amount) throws RemoteException
{
// Method implementation
throw new RemoteException();
}
//Remainder of class definition
}
一个方法可以声明抛出多个异常,多个异常之间用逗号隔开。
10.3 自定义异常
注意:
- 所有异常都必须是 Throwable 的子类。
- 如果希望写一个检查性异常类,则需要继承 Exception 类。
- 如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。
可以重写带参构造,设置异常原因。
自定义异常只能手动抛出,不能让JVM抛。
10.4 日志记录(扩展)
需要把异常的堆栈信息保存,不能只停留在控制台。从而更方便的定位错误
日志:错误日志、正常的操作信息、SQL日志、业务日志
Apache公司的开源项目log4j
使用步骤
1、下载jar 包 --java的class文件的压缩包
2、添加jar包项目
3、配置文件 . propreties .xml log4j.properties
propreties 以键值对方式存储 key=value
4、代码中引用
第11章 集合
11.1集合
什么是集合框架:一些接口和类。java.util包下
集合框架图:来源->菜鸟教程
Collection 集合 单列集合的根接口
- List 列表
- 特点:
- 有序、访问顺序和插入顺序一致
- 有索引
- 允许重复
- 允许null 元素
- 具体的实现类
- ArrayList:可变长的数组
- 此实现不是同步的,不是线程安全
- 好处: 访问、修改速度快
- 缺点: 添加、删除速度慢,浪费空间
- 使用场景:查询、遍历(频繁增删不建议使用)
- LinkedList:双向链表
- 随机访问效率低
- 增删快
- 使用场景与ArrayList正好相反
- ArrayList:可变长的数组
- 特点:
- Set 数据集,集合
- 特点
- 不包含重复元素
- 最多包含一个 null 元素
- 不要求有序
- 具体实现子类
- HashSet
- LinkedHashSet
- TreeSet
Map 关系、映射
- 特点
- Key – Value (键值对的形式存在特殊的)
- 一个映射不能包含重复的键,键唯一
- 子类有HashMap
允许使用 null 值和 null 键
11.2 泛型
泛型方法
定义泛型方法的规则(来源:菜鸟教程):
- 所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前。
- 每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
- 类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
- 泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)。
// 泛型方法 printArray
public static < E > void printArray( E[] inputArray )
{
// 输出数组元素
for ( E element : inputArray ){
System.out.printf( "%s ", element );
}
System.out.println();
}
泛型类
泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。
public class Box<T> {
private T t;
public void add(T t) {
this.t = t;
}
public T get() {
return t;
}
类型通配符
- 类型通配符一般是使用 ? 代替具体的类型参数
import java.util.*;
public class GenericTest {
public static void main(String[] args) {
List<String> name = new ArrayList<String>();
List<Integer> age = new ArrayList<Integer>();
List<Number> number = new ArrayList<Number>();
name.add("icon");
age.add(18);
number.add(314);
getData(name);
getData(age);
getData(number);
}
public static void getData(List<?> data) {
System.out.println("data :" + data.get(0));
}
}
- 类型通配符上限通过形如List来定义,如此定义就是通配符泛型值接受Number及其下层子类类型。
import java.util.*;
public class GenericTest {
public static void main(String[] args) {
List<String> name = new ArrayList<String>();
List<Integer> age = new ArrayList<Integer>();
List<Number> number = new ArrayList<Number>();
name.add("icon");
age.add(18);
number.add(314);
//getUperNumber(name);//1
getUperNumber(age);//2
getUperNumber(number);//3
}
public static void getData(List<?> data) {
System.out.println("data :" + data.get(0));
}
public static void getUperNumber(List<? extends Number> data) {
System.out.println("data :" + data.get(0));
}
}
- 类型通配符下限通过形如 List<? super Number>来定义,表示类型只能接受Number及其三层父类类型
第12章 IO、序列化、Properties集合
File 表示文件、文件夹
12.1 IO流
流 是内存和硬盘数据交互的一个通道/桥梁 java.io
流的分类:
- 按照数据的流向分:
- 输入流:文件->内存 (读)
- 输出流:内存->文件 (写)
- 按照数据交互的单元划分:
- 字节流:一次一个字节
- 字符流:一次一个字符
字符流最终还是以字节流方式实现的
字节流:
- 字节输入流
InputStream 此抽象类是表示字节输入流的所有类的超类。- abstract int read() 从输入流中读取数据的下一个字节. 返回值:下一个数据字节;如果到达流的末尾,则返回 -1。
- int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。(返回读取的长度)
- int read(byte[] b, int off, int len) 将输入流中最多 len 个数据字节读入 byte 数组。
- void close() 关闭此输入流并释放与该流关联的所有系统资源。
- FileInputStream 从文件系统中的某个文件中获得输入字节
- FileInputStream(File file)
- FileInputStream(String name)
- BufferedInputStream 在创建 BufferedInputStream 时,会创建一个内部缓冲区数组。
- BufferedInputStream(InputStream in) 创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
- BufferedInputStream(InputStream in, int size) 创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
- 字节输出流
OutputStream 抽象类表示输出字节流的所有类的超类- abstract void write(int b) 将指定的字节写入此输出流。
- void write(byte[] b) 将 b.length 个字节从指定的 byte 数组写入此输出流。
- void write(byte[] b, int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
- void close() 关闭此输出流并释放与此流有关的所有系统资源。
- void flush() 刷新此输出流并强制写出所有缓冲的输出字节。
- FileOutputStream文件输出流,用于将数据写入 File 或 FileDescriptor 的输出流
- FileOutputStream(File file) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
- FileOutputStream(File file, boolean append) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。(true追加,false擦除重写)
- FileOutputStream(String name) 创建一个向具有指定名称的文件中写入数据的输出文件流。
- FileOutputStream(String name, boolean append) 创建一个向具有指定 name 的文件中写入数据的输出文件流。
- BufferedOutputStream 该类实现缓冲的输出流
- BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
- BufferedOutputStream(OutputStream out, int size) 创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流。
字符流:
- 字符输入流
Reader 用于读取字符流的抽象类- int read() 读取单个字符。
- int read(char[] cbuf)将字符读入数组。返回读取字符的个数
- abstract int read(char[] cbuf, int off, int len) 将字符读入数组的某一部分。
- InputStreamReader 字节—>字符 字节流通向字符流的桥梁
- 自带缓冲区 8192,可以自定义编码格式来解码
- InputStreamReader(InputStream in) 创建一个使用默认字符集的 InputStreamReader。
- InputStreamReader(InputStream in, String charsetName) 创建使用指定字符集的 InputStreamReader。
- FileReader 用来读取字符文件的便捷类。
- FileReader(String fileName) 在给定从中读取数据的文件名的情况下创建一个新 FileReader。
- FileReader(File file)在给定从中读取数据的 File 的情况下创建一个新 FileReader。
- BufferedReader 提高字符流的读取速度
- String readLine() 读取一个文本行。
- 包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null
- 字符输出流
Writer 用于写字符流的抽象类- void write(char[] cbuf) 写入字符数组。
- abstract void write(char[] cbuf, int off, int len)写入字符数组的某一部分。
- void write(int c) 写入单个字符。
- void write(String str) 写入字符串。
- oid write(String str, int off, int len) 写入字符串的某一部分。
- OutputStreamWriter 是字符流通向字节流的桥梁 字符—>字节
- OutputStreamWriter(OutputStream out) 创建使用默认字符编码的 OutputStreamWriter。
- FileWriter 用来写入字符文件的便捷类。
- FileWriter(File file) 根据给定的 File 对象构造一个 FileWriter 对象
- FileWriter(File file, boolean append) 根据给定的 File 对象构造一个 FileWriter 对象。
- FileWriter(String fileName) 根据给定的文件名构造一个 FileWriter 对象。
- BufferedWriter 提高字符流的写入速度
- void newLine() 写入一个行分隔符。
操作java基本数据类型
DataInputStream
DataOutputStream
用法与文件输入输出流类似,只不过操作的是基本数据类型
操作Object对象
- ObjectInputStream
Object readObject() 从 ObjectInputStream 读取对象。 - ObjectOutputStream(被写入的对象要序列化,否则会出现异常)
void writeObject(Object obj) 将指定的对象写入 ObjectOutputStream。
System.in----InputStream 标准的字节输入流
System.out----PrintStream print方法:以字符串的形式输出
PrintWriter 以字符串的形式输出
12.2 序列化
序列化
java对象----------->字节
实现方式:
- 方式1:
- 实现Serializable接口 ,此接口只是一个标记,表名此类型的对象可以序列化
- 也可以实现部分属性的序列化 ,不需要序列化的字段前面加上transient
- 父类实现了序列化,子类可以继承,无需再实现此接口
- 静态的属性也是不序列化的,静态的内容是属于类的
- 方式2:
- 实现Externalizable接口,可以实现部分属性的序列化
作用:方便存储对象以及方便在网络间传输。
反序列化
字节-------->java对象
ObjectInputStream类的readObject() 方法读取文件返回java对象。
可能会出现InvalidClassException,原因是因为serialVersionUID不一致了
serialVersionUID :
- 识别序列化字节和内存中类的版本的
- 根据类中的属性和方法综合计算来的一个long值,也就是类中属性或者方法有改动,long值就会改变
serialVersionUID的作用:主要用在序列化列升级的时候(属性和方法有改动的时候)
- 如果让当前版本兼容之前的版本,需要使用之前的序列号
- 如果不想让当前版本兼容之前的版本,改变序列号
作用:方便恢复/重建对象
12.3 Properties 集合
xxx .properties文件 键值对形式存在
用于操作 java中的配置文件 properties/xml
Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。
- 继承Hashtable<K,V>,Hashtable和HashMap类似,唯一的区别就是Hashtable是线程安全的
- 属性列表中每个键及其对应值都是一个字符串。
-
String getProperty(String key)
-
setProperty(String key, String value)
-
void store(Writer writer, String comments)
-
void store(OutputStream out, String comments)
-
void load(Reader reader)
-
void load(InputStream inStream)
特点:
- 该集合不能写泛型
- 可以持久化的属性集。键值可以存储到集合中,也可以存储到硬盘、U盘等
- 可以和IO流有关的技术结合使用
第13章 多线程
13.1 多线程
多线程宏观上看是多个任务同时进行,实际是CPU时间片轮转。
什么是进程:
一个应用程序的实例。当我们启动一个应用程序的时候,就创建该程序的实例,其实开启了一个进程就会分配内存、硬盘、摄像头等等这些电脑资源。----------------------------进程是资源分配的基本单位
什么是线程:
运行在进程之中。进程中可以开启线程。线程来完成这个进程的一些任务。-----------线程是CPU进行调度的基本单位
一个进程中至少有一个线程,通常叫做主线程。main方法就是主线程的入口。
一个进程中可以有多个线程,除了主线程之外,其它的线程是由主线程中创建的。
多线程实现的两种方式:
方式一:将类声明为 Thread 的子类,该子类应重写 Thread 类的 run 方法
方式二:声明实现 Runnable 接口的类。该类然后实现 run 方法(推荐,因为可以实现多个接口)
启动方式:
- run() —线程体,在其中描述线程要执行的任务(用该方法只是单纯的使用方法,不是开启线程执行)
- start() ----使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
两种方式的比较:
- 方式一通过继承 | 简单 | 继承是单继承(局限)
- 方式二是通过接口,重写run方法 | 接口是可以多实现(灵活) | 实现资源共享
多线程的状态
-
Thread.sleep(毫秒); 休眠
调用此方法,该线程会进入休眠状态,(阻塞),当休眠时间结束,线程继续运行
可能引发InterruptedException
使用场景:模拟任务消耗的时间 -
join() 加入,插入,插队
join(毫秒) 插队多少毫秒
实例方法,谁调用谁插队,等t线程执行完,其它线程才执行
可能引发InterruptedException -
Thread.yield() 礼让
该代码所在线程礼让
放弃本次抢占到的时间片,重写进行下一次的抢占,可能礼让成功,也可能礼让不成功 -
wait();//等待,等待当前对象调用notify或者notifyAll,否则就出于阻塞状态
-
notify();//唤醒当前对象,解除阻塞
线程同步问题:
多线程共享资源,会引发数据不一致,为了解决这个问题可以让线程同步。
解决方案:加锁–把读写作为一个整体加锁
-
同步代码块
synchronized(对象锁) { }
每一个对象都有唯一的一把锁 -
同步方法
一个方法内部只有一个同步代码块,那么我们经常写成同步方法,也就是把synchronized加在方法上,同步方法的锁,都是this,当前对象的锁
同步代码块和同步方法的区别:
- 一个方法内部可以出现多个同步代码块,且同步代码块可以嵌套
- 一个线程在访问同步方法,其它的线程都不能访问该对象所有的同步方法,只能访问非同步的方法
- 同步代码块更灵活,同步方法可以重用
同步的特点:
- 线程安全(多线程访问下,数据是正确的)
- 效率低(不提倡把耗时的操作加同步)
线程安全相关的类:
StringBuilder 非线程安全:效率高,适合单线程
StringBuffer 线程安全的:效率低,适合多线程
HashMap 非 线程安全 key可以null
Hashtable 线程安全 key不能为null
ArrayList 数组列表 非线程安全
Vector 向量 线程安全
13.2 线程池
使用线程池的原因:
- 线程池,统一管理线程,减少创建线程、销毁线程的操作,线程的使用率就高
- 定时、定期执行一定的任务
如何使用线程池:
在包java.util.concurrent下
-
顶级接口Executor 执行已提交的 Runnable 任务的对象
- 子接口ExecutorService
- 抽象类AbstractExecutorService
实现类:ThreadPoolExecutor - 接口ScheduledExecutorService
实现类:ScheduledThreadPoolExecutor
- 抽象类AbstractExecutorService
- 子接口ExecutorService
-
工具类Executors
作用:创建线程池对象- static ExecutorService newCachedThreadPool() 创建可缓存线程的线程池
- static ExecutorService newFixedThreadPool(int nThreads) 创建固定线程数的线程池
- static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 创建周期性执行的线程池
- static ExecutorService newSingleThreadExecutor() 创建单线程的线程池
-
自定义线程池对象
- public ThreadPoolExecutor(int corePoolSize, 核心线程数
- int maximumPoolSize, 最大线程数
- long keepAliveTime, 最大空闲时间
- TimeUnit unit,最大空闲时间 使用的单位
- BlockingQueue workQueue,阻塞队列,等待的队列
- ThreadFactory threadFactory, 线程工厂 ,创建线程
- RejectedExecutionHandler handler)拒绝策略
第14章 网络编程
14.1 网络基础
OSI模型:
应用层、表示层、会话层、传输层、网络层、数据链路层、物理层(高—>低,高三层低四层)
TCP/IP模型:
应用层(HTTP/数据)、传输层(分组、端到端/TCP、UDP/数据包)、网络层(路由、IP/数据帧)、网路接口层(二进制数据)
常用协议:
-
TCP协议:建立在连接的基础上(面向连接)。传输数据之前,两端(客户端和服务端)首先要建立连接。
- 建立连接:经过3次握手
1、客户端发送请求
2、服务端响应,告诉客户端,我收到请求
3、客户端发送确认请求 - 断开连接:经过4次挥手
- 提供的是可靠的数据传输。传输速度有限。端到端的数据传输。
- 使用场景:传输的数据要求可靠。比如发送图片等文件
- 建立连接:经过3次握手
-
UDP协议:无连接的。发送的数据直接发。
不可靠。传输速度很快。支持一对一、一对多、多对多多的数据传输。
使用场景:对传输速度要求高,对结果不可靠影响不大的。比如腾讯会议 -
IP:IP地址:网络上的主机的唯一标识
IPV4:4个字节(32位
IPV6:16个字节(128位)
域名:www.baidu.com—dns----xx.xx.xx.xx(dns即域名解析服务) -
端口号:对软件的逻辑标识
系统都会默认分配一个端口号
程序也可以自己指定端口号
0-65535 0-1024已被占用
14.2 网络编程
使用包-------java.net
14.2.1 TCP:
- 客户端
Socket 此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点,操作的是字节流-
Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号。
-
InputStream getInputStream() 返回此套接字的输入流。
-
OutputStream getOutputStream() 返回此套接字的输出流。
-
void shutdownOutput() 禁用此套接字的输出流。
对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列 -
void close()关闭此套接字。
-
- 服务端(应该先启动,否则客户端会报异常ConnectException)
- ServerSocket 此类实现服务器套接字。服务器套接字等待请求通过网络传入
- ServerSocket(int port) 创建绑定到特定端口的服务器套接字。此方法在连接传入之前一直阻塞。
- Socket accept() 侦听并接受到此套接字的连接。
服务器端没有自己的字节流,那怎么用?他就用客户端的流和相应的客户端进行交互
客户端-服务器 多-1
A input output
B input output
C input output - void close() 关闭此套接字
文件上传/下载:
- 上传 Client—>Server
- Client
C:\Users\86136\Desktop\img\1.jpg
1、把文件读到内存 FileInputStream
2、内存通过网洛传输到Server OutputStream - Server
1、读Socket InputStream
2、写入文件 FileOutputStream
- Client
- 下载 Client<—Server
- Server
C:\Users\86136\Desktop\img\1.jpg
1、把文件读到内存 FileInputStream
2、内存通过网洛传输到Client OutputStream - Client
1、读Socket InputStream
2、写入文件 FileOutputStream
- Server
14.2.2 UDP:
- DatagramSocket 来发送和接收数据报包的套接字
每个在数据报套接字上发送或接收的包都是单独编址和路由的。为了接收广播包,应该将 DatagramSocket 绑定到通配符地址。在某些实现中,将 DatagramSocket 绑定到一个更加具体的地址时广播包也可以被接收。- DatagramSocket() 构造数据报套接字并将其绑定到本地主机上任何可用的端口。 —发送
- DatagramSocket(int port) 创建数据报套接字并将其绑定到本地主机上的指定端口。 —接收
- DatagramSocket(int port, InetAddress laddr) 创建数据报套接字,将其绑定到指定的本地地址。
- void receive(DatagramPacket p) 从此套接字接收数据报包。
- void send(DatagramPacket p) 从此套接字发送数据报包。 此方法在接收到数据报前一直阻塞
- DatagramPacket 数据报包
- DatagramPacket(byte[] buf, int length) 构造 DatagramPacket,用来接收长度为 length 的数据包。
- DatagramPacket(byte[] buf, int length, InetAddress address, int port)
- byte[] getData() 返回数据缓冲区。
- int getLength() 返回将要发送或接收到的数据的长度。
- int getPort() 返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的。
- InetAddress getAddress() 返回某台机器的 IP 地址,此数据报将要发往该机器或者是从该机器接收到的。
- InetAddress 此类表示互联网协议 (IP) 地址。IP 地址是 IP 使用的 32 位或 128 位无符号数字
- static InetAddress getByName(String host) 在给定主机名的情况下确定主机的 IP 地址。
- String getHostName() 获取此 IP 地址的主机名。
14.2.3 http编程(基于TCP):
通常用于B/S模式,即浏览器/服务器。
URL:统一资源定位符------------URLConnection openConnection() 返回一个 URLConnection 对象,它表示到 URL 所引用的远程对象的连接。
URLConnection:应用程序和 URL 之间的通信链接
URLConnection的使用步骤
- 通过在 URL 上调用 openConnection 方法创建连接对象。
- 处理设置参数和一般请求属性。
- 使用 connect 方法建立到远程对象的实际连接。
- 远程对象变为可用。远程对象的头字段和内容变为可访问。
方法:
abstract void connect() 打开到此 URL 引用的资源的通信链接
InputStream getInputStream() 返回从此打开的连接读取的输入流。
void setUseCaches(boolean usecaches) 将此 URLConnection 的 useCaches 字段的值设置为指定的值。
void setConnectTimeout(int timeout) 设置一个指定的超时值(以毫秒为单位),该值将在打开到此 URLConnection 引用的资源的通信链接时使用。
void setRequestProperty(String key, String value) 设置一般请求属性。
HttpURLConnection(URLConnection的子类):
abstract void disconnect() 指示近期服务器不太可能有其他请求。
int getResponseCode() 从 HTTP 响应消息获取状态码。
第15章 其他
15.1 XML(Extensible Markup Language)
xml 标签是可以自定义的(标记语言)
语法是严格的。
区分大小写的
有且只有一个根标签
用于存储数据(配置文件)、传输数据
节点类型:元素Element 属性Attribute 文本Text 注释<!-- -->
转义字符:< ---- <
>---->
或者把数据直接放在<![CDATA[ ]]>
解析XML的两种方式:
- 方式1:DOM(Document Object Model) 文档对象模型 树模型 层次模型
- 优点:整个文档都存在内存中,随机访问或操作节点方便理解简单
缺点:文档特别大,文档结构复杂,解析时效率低,消耗内存 - 要点:
- Document doc=builder.parse(new File(“stu.xml”)); -----------------doc是Xml文档对象
- Element root=doc.getDocumentElement();//获取根节点
- NodeList getChildNodes();
- getAttribute(“id”) 根据属性名拿到属性值
- DOM4J (DOM for java) 只局限于java
- 添加jar包、添加到build path
- SAXReader reader=new SAXReader();
- Document doc=reader.read(path);
- getRootElement(); 获取根节点
- elements(); 获取所有的子元素
- element(子标签名) 根据标签名获取子元素
- attributeValue(属性名) 根据属性名获取属性值
- 优点:整个文档都存在内存中,随机访问或操作节点方便理解简单
- 方式2:SAX
- 不加载DOM树,从上往下扫描文档,节省内存,靠事件驱动
- 优点:节省内存,灵活,效率相对高
缺点:做不到随机访问
15.2 单元测试(junit)
黑盒测试:不关注代码,只关注功能。在什么场景下,点击了什么按钮,有没有什么反应。功能测试。
白盒测试:写部分代码。脚本。自动化测试。
单元测试步骤:
- 添加jar包(eclipse自带,idea需要导入包)
- 测试类一般放置在单独的测试包中,命名方式:XXXTest/TestXXX
- 测试方法是可以独立运行的,由jvm调用,需要添加注解@Test。测试方法没有返回值。测试方法命名方式:test方法名
- 运行测试方法:
- 运行单个测试方法:光标放在方法名这一行,右键选择run as -Junit Test
- 运行所有测试方法:光标放在空白处,右键选择run as -Junit Test
- 测试结果:绿色—通过 红色–不通过
- 测试类的生命周期
- @BeforeClass
@Before
@Test
@After
@AfterClass
- @BeforeClass
第16章 反射
16.1 什么是反射
反射 (Reflection) 是 Java 的特征之一,它允许运行中的 Java 程序获取自身的信息,并且可以操作类或对象的内部属性。
通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。程序中一般的对象的类型都是在编译期就确定下来的,而 Java 反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。
反射的核心
反射的核心是 JVM 在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。
Java 反射主要提供以下功能:
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
- 在运行时调用任意一个对象的方法
注意;是运行时而不是编译时。
16.2 反射的主要用途
反射最重要的用途就是开发各种通用框架
很多框架(比如 Spring)都是配置化的(比如通过 XML 文件配置 Bean),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射,运行时动态加载需要加载的对象。
16.3 反射的基本运用
反射相关的类一般都在 java.lang.relfect 包里
16.3.1 获得Class对象
方法有三种:
方法1:
使用Class类的forName静态方法:(通过名字创建类的Class对象;)
比如在 JDBC 开发中常用此方法加载数据库驱动:
Class.forName(driver);
方法2:
直接获取某一个对象的 class
Class<?> klass = int.class;
Class<?> classInt = Integer.TYPE;
方法3:
调用某个对象的 getClass() 方法
StringBuilder str = new StringBuilder("123");
Class<?> klass = str.getClass();
16.3.2 判断是否为某个类的实例
一般地,我们用 ==instanceof ==关键字来判断是否为某个类的实例。同时我们也可以借助反射中 Class 对象的 isInstance() 方法来判断是否为某个类的实例,它是一个 native 方法;
instanceof :左边的对象是否是它右边的类的实例,返回boolean类型的数据
String s = "I AM an Object!";
if(s instanceof Object){
system.out.println("true");
}
isInstance()方法:确定指定的对象赋值兼容此Class所表示的对象。它与Java语言instanceof运算符的动态等效(编译时这边的对象可以不确定类型,是class)
public native boolean isInstance(Object obj);
例
Class cls = Long.class;
Long l = new Long(86576);
Double d = new Double(3.5);
// checking for Long instance
boolean retval = cls.isInstance(l);
16.3.3 创建实例
通过反射来生成对象主要有两种方式。
- 使用Class对象的newInstance()方法来创建Class对象对应类的实例。
Class<?> c = String.class;
Object str = c.newInstance();
- 先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。这种方法可以用指定的构造器构造类的实例。
//获取String所对应的Class对象
Class<?> c = String.class;
//获取String类带一个String参数的构造器
Constructor constructor = c.getConstructor(String.class);
//根据构造器创建实例
Object obj = constructor.newInstance("23333");
System.out.println(obj);
16.3.4 获取方法
获取某个Class对象的方法集合,主要有以下几个方法:
方法1:
getDeclaredMethods 方法返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
public Method[] getDeclaredMethods() throws SecurityException
方法2:
getMethods 方法返回某个类的所有公用(public)方法,包括其继承类的公用方法。
public Method[] getMethods() throws SecurityException
方法3:
getMethod 方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象。
public Method getMethod(String name, Class<?>... parameterTypes)
16.3.5 获取构造器信息
获取类构造器的用法与上述获取方法的用法类似。主要是通过Class类的getConstructor方法得到Constructor类的一个实例,而Constructor类有一个newInstance方法可以创建一个对象实例。
16.3.6 获取类的成员变量(字段)信息
- getFiled:访问公有的成员变量
- getDeclaredField:所有已声明的成员变量,但不能得到其父类的成员变量
getFileds 和 getDeclaredFields 方法用法同上(参照 Method)。
16.3.7 调用方法
当我们从类中获取了一个方法后,我们就可以用 invoke()
方法来调用这个方法
public class test1 {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<?> klass = methodClass.class;
//创建methodClass的实例
Object obj = klass.newInstance();
//获取methodClass类的add方法
Method method = klass.getMethod("add",int.class,int.class);
//调用method对应的方法 => add(1,4)
Object result = method.invoke(obj,1,4);
System.out.println(result);
}
}
class methodClass {
public final int fuck = 3;
public int add(int a,int b) {
return a+b;
}
public int sub(int a,int b) {
return a+b;
}
}
16.3.8 利用反射创建数组
数组在Java里是比较特殊的一种类型,它可以赋值给一个Object Reference。
public static void testArray() throws ClassNotFoundException {
Class<?> cls = Class.forName("java.lang.String");
Object array = Array.newInstance(cls,25);
//往数组里添加内容
Array.set(array,0,"hello");
Array.set(array,1,"Java");
Array.set(array,2,"fuck");
Array.set(array,3,"Scala");
Array.set(array,4,"Clojure");
//获取某一项的内容
System.out.println(Array.get(array,3));
}
其中的Array类为java.lang.reflect.Array类。我们通过Array.newInstance()创建数组对象,它的原型是:
public static Object newInstance(Class<?> componentType, int length)
throws NegativeArraySizeException {
return newArray(componentType, length);
}
而 newArray 方法是一个 native 方法,它在 HotSpot JVM 里的具体实现我们后边再研究,这里先把源码贴出来:
private static native Object newArray(Class<?> componentType, int length)
throws NegativeArraySizeException;
源码目录:openjdk\hotspot\src\share\vm\runtime\reflection.cpp
arrayOop Reflection::reflect_new_array(oop element_mirror, jint length, TRAPS) {
if (element_mirror == NULL) {
THROW_0(vmSymbols::java_lang_NullPointerException());
}
if (length < 0) {
THROW_0(vmSymbols::java_lang_NegativeArraySizeException());
}
if (java_lang_Class::is_primitive(element_mirror)) {
Klass* tak = basic_type_mirror_to_arrayklass(element_mirror, CHECK_NULL);
return TypeArrayKlass::cast(tak)->allocate(length, THREAD);
} else {
Klass* k = java_lang_Class::as_Klass(element_mirror);
if (k->oop_is_array() && ArrayKlass::cast(k)->dimension() >= MAX_DIM) {
THROW_0(vmSymbols::java_lang_IllegalArgumentException());
}
return oopFactory::new_objArray(k, length, THREAD);
}
}
另外,Array 类的 set 和 get 方法都为 native 方法,在 HotSpot JVM 里分别对应 Reflection::array_set 和 Reflection::array_get 方法,这里就不详细解析了。
16.4 反射的一些注意事项
由于反射会额外消耗一定的系统资源,因此如果不需要动态地创建一个对象,那么就不需要用反射。
另外,反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。