由于自己忘性比较大,最初学习的时候也没有做笔记,因此后期学习吃力,对于前期学习的知识进行笔记重新整理与学习,巩固加强,温故而知新、可以为师矣。
1.java异常
异常:在java语言中,将程序执行中发生的不正常情况称为异常,java语言中的异常用于处理非预期的情况,例如文件没找到、网络错误、非法参数等。
1.1 异常的大致分类:错误与异常
异常是程序编译运行时发生的某种异常,程序员应手动处理
错误是程序运行期间发生了某种错误,通常没有具体的处理方式,程序会结束运行
1.2 java异常类:
1.3 异常处理机制:抓抛模型
程序员只能处理异常对error无法处理
a:JVM的默认处理方式
把异常的名称,原因,位置等信息输出在控制台,同时会结束程序。
一旦有异常发生,其后来的代码不能继续执行。
b:解决程序中异常的手动方式
a):编写处理代码 try…catch…finally
b):抛出 throws
java程序的执行过程中如出现异常,会自动生成一个一场对象,该异常对象将被提交给java运行时系统,这个过程为抛出异常throws。
//例子1:使用throws抛出异常
public static void main(String[] args) throws IOException {
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
String res = bf.readLine();
}
如果一个方法内抛出异常,该异常会被抛到调用方法中。如果异常没有在调用的方法中处理,将异常抛给方法的调用者,该过程一致继续,直到异常被处理,这个过程称之为catch异常。
使用try{}catch()对于信息进行捕获时,打印异常信息,getMessage()方法,用来得到有关异常事件的信息,printStackTrace()用来跟踪异常事件发生时执行堆栈的内容。finally是一个可选的代码块,一定会被执行的代码块
//例子2:使用try{}catch(){}抓取异常
public static void main(String[] args){
try {
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
String res = bf.readLine();
} catch (IOException e) {
e.printStackTrace();
}
}
父子继承关系的异常,父类方法抛出异常,子类重写方法时也要抛出异常并且子类不能抛出比父类范围更大的异常类型。 用户也可以自定义异常类。
1.4 throw关键字
何时使用throw关键字?(方法内部抛出对象关键字)
当方法接受的参数判断不合法时,可以使用throw将异常消息抛出给调用者。使用的格式:throw new 异常类名(参数);throw的使用范围是方法的内部
public static void main(String[] args){
System.out.println(testThrow(9, 0));
}
public static int testThrow(int a,int b){
if(b==0){
throw new ArithmeticException("除数不能为0");
}else{
return a/b;
}
}
//结果是:java.lang.ArithmeticException: 除数不能为0
1.5 Throws 关键字
throws(方法声明异常关键字)
方法将问题使用throws表示出来让调用者去处理。使用的格式是:修饰符 返回值 方法名(参数) throws 异常类名..{}(一般有问题使用try{}catch(){}finally{})
1.6 try..chatch(){}
try{}块:其中写入可能产生异常的代码
catch(){}:用来进行某种异常的捕获,实现对捕获到的异常进行处理
finally{}:可选,无论异常是否发生都会执行,一般用于数据库文件释放资源等。
使用多个catch块捕获不同的异常时应注意异常之间的顺序,异常之间存在平级关系和上下级关系,越高级的父类越在最后的catch中
1.7 方法重写时的异常处理
class Fu{
public void method() throws RuntimeException{
}
}
//子类重写父类方法时,抛出与父类相同的异常
//父类方法没有异常声明时。子类覆盖也无异常声明
//如果遇到实现子类的方法中发生了异常,不能使用throws进行抓取,只能使用catch进行捕获,catch处理不了就使用throw抛出一个RuntimeException的子类(不是子类的强转)
class Sun extends Fu{
@Override
public void method() throws RuntimeException {
}
}
1.8 Throwable
常用的方法有:
getMessage() 返回该异常的详细信息字符串
toString() 返回该异常的名称与详细信息字符串
printStackTrace()控制台输出该异常的名称与详细信息字符串、异常出现的代码位置
1.9 自定义异常
每个异常都调用父类构造方法,把异常信息传递给父类,父类对信息进行封装
自定义异常简单例子:
**
* @author itxing
* @create 2020/6/1-maven_javaweb
*/
public class ErrorTest {
public static void main(String[] args){
try {
Person p = new Person("星仔",25);
testPersonAge(p);
}catch (MyException ex){
ex.printStackTrace();
}
}
public static void testPersonAge(Person p) throws MyException {
if(p.getAge()>0&&p.getAge()<150){
System.out.println(p.getName()+"的年龄是:"+p.getAge());
}else{
throw new MyException("年龄不合法,老哥!");
}
}
}
class Person{
private String name;
private int age;
Person(){}
Person(String name,int age){
this.name = name;
this.age = age;
}
public String getName(){
return this.name;
}
public int getAge(){
return this.age;
}
public void setName(String name){
this.name = name;
}
public void setAge(int age){
this.age = age;
}
}
//自定义异常,可以继承RuntimeException也可以继承Exception
class MyException extends Exception{
public MyException(){
}
public MyException(String vars){
super(vars);
}
}
2.java的集合框架:
注:在初次学习时,先学会使用工具,对于工具的制作以及工具的原理随后深入学习时进行总结与思考。
2.1 集合介绍
集合是java中提供的一种容器,可以用来存储多个数据。集合是一种容器,是容器就有如何放置数据到容器中、如何从容器中获取数据,如何将容器中数据修改,如何将数据从容器中清除,也就是(CRUD)
数组与集合的区别:
数组长度是固定的,集合的长度是可变的
集合中存储的元素必须是引用类型的数据
2.2 集合的体系结构图(常用类)
2.3 List
2.3.1 介绍
List接口是一个元素存取有序,用户可以通过元素的整数索引访问元素,并搜索列表中的元素,带有索引的解,集合中可以有重复的元素,使用equals判断。
常用类:ArrayList、LinkedList、Vector
2.3.2 List常用方法
list的增删改查:
1.add(Object e):向集合末尾处,添加指定的元素
add(int index, Object e):向集合指定索引处,添加指定的元素,原有元素依次后移
2.remove(Object e):将指定元素对象,从集合中删除,返回值为被删除的元素
remove(int index):将指定索引处的元素,从集合中删除,返回值为被删除的元素
3.et(int index, Object e):将指定索引处的元素,替换成指定的元素,返回值为替换前的元素
4.get(int index):获取指定索引处的元素,并返回该元素
list的使用例子:
public static void main(String[] args){
List list= new ArrayList();
//添加元素
list.add(new Person("itxing",25));
list.add(new Person("xingzai",23));
list.add(new Person("lisi",23));
//在指定位置天剑
list.add(1,new Person("zhangsan",30));
//修改指定的元素的值
list.set(2,new Person("wangwu",32));
//删除指定位置的元素
list.remove(1);
//查询指定位置的元素
list.get(2);
Iterator iterator = list.iterator();
while(iterator.hasNext()){
Person next = (Person)iterator.next();
System.out.println(next.getName()+" "+next.getAge());
}
//使用iterator遍历集合
//使用索引遍历集合
// for(int i=0;i<list.size();i++){
// Person p = (Person)list.get(i);
// System.out.println(p.getName()+" "+p.getAge());
// }
}
注意:
使用Iterator迭代时,不能操作数组元素,会出现并发异常,可以使用ListIterator迭代器操作元素
2.3.3 子类对比
ArrayList集合存储的数据结构是数组结构,元素的增删慢,查找快,多线程下不安全,用于查询多的场景
LinkedList数据结构是链表结构,方便元素的添加和删除,用于首位操作的场景
Vector集合的数据结构也是数组结构,线程安全,效率低
注:后期对集合类进行源码阅读学习。
2.4 Set
2.4.1 简单介绍
set不能够放重复的元素,存放时会通过equals判断元素是否相同
2.4.2 Set常用方法
添加的实例:
public static void main(String[] args){
Set set = new HashSet();
set.add(new Person("itxing",25));
set.add(new Person("ll",30));
set.add(new Person("kk",28));
set.add(new Person("itxing",25));//与第一条参数值相同,但是会加入到集合中,因为地址不同是两个对象
Iterator iterator = set.iterator();
while(iterator.hasNext()){
Person next = (Person) iterator.next();
System.out.println(next.getName()+" "+next.getAge());
}
}
//结果是将元素全部添加到set中,对于引用类型需要重写equals方法,否则会使用父类的equals方法
//父类的方法,比较的是对象的地址值
public boolean equals(Object var1) {
return this == var1;
}
set的简单应用
public static void main(String[] args){
Set set = new HashSet();
//添加元素
set.add(new Person("itxing",25));
set.add(new Person("ll",30));
set.add(new Person("kk",28));
Person p = new Person("itxing",25);
set.add(p);
//判断集合是否存在该元素
System.out.println(set.contains(p));
//删除元素
System.out.println(set.remove(p));
//遍历集合
Iterator iterator = set.iterator();
while(iterator.hasNext()){
Person next = (Person) iterator.next();
System.out.println(next.getName()+" "+next.getAge());
}
}
2.4.3 常用子类
先介绍一下hash表:hash表底层也是数组机制,数组中存放对象,对象的存储位置是使用hash算法求的元素的位置,这样的结构为hash数组,一个对象存放在hash表中时先调用Object的hashCode方法,算出对象在表中存放的位置,如果对象的hash值相同称之为hash冲突,此时调用equals方法,比较两个元素是否为同一地址的对象,如果是同一对象就不将元素放入数组中,否则将其放入。
hash冲突的解决办法:
1.开放定址法:采用某种方法继续探测哈希表中的其他存储位置,知道有空位置为止h=(key+di)%m;di是一个增量,增量的方式有:线性探测再散列、二次探测再散列、伪随机再散列
2.再哈希法:h0= hash(K);h1= hsah(h0)…
3.链地址法:同一个位置使用链表结构将元素放置
4.建立公共溢出区:另外设置存储空间用来存储冲突记录
HashSet:底层是hash表,元素唯一,不能重复,元素的存与取的顺序不能保证一致
LinkedHashSet:元素唯一不能重复,能够保证元素以输入进去的顺序存储。哈希表结构 + 链表结构
TreeSet:以自然顺序存储元素,并且元素不重复
集合判断元素唯一的原理:
ArrayList的contains方法会使用调用方法时,传入的元素的equals方法依次与集合中的旧元素所比较,从而根据返回的布尔值判断是否有重复元素。此时,当ArrayList存放自定义类型时,由于自定义类型在未重写equals方法前,判断是否重复的依据是地址值,所以如果想根据内容判断是否为重复元素,需要重写元素的equals方法。
2.5 Map
与collection的不同,collection中的元素是孤立的,向集合中存储元素采用一个个元素的方式存储。Map中的集合,元素是成对存在的,是KEY -VALUE的一种形式,通过键可以找到所对应的值。Map集合不包含重复的键,值可以重复;每个键只能对应一个值。
主要分析HashMap和LinkedHashMap。
HashMap中的主要方法:
HashMap的增删改查方法:
public static void main(String[] args) {
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("aa",10);
map.put("bb",15);
map.put("cc",20);
map.put("dd",25);
// System.out.println(map);
//修改元素
map.put("bb",30);
//获取元素
System.out.println(map.get("aa"));
//移除元素
map.remove("dd");
// System.out.println(map);
//map集合遍历键找值,keySet函数用于返回Map集合中所有的键值对Entry对象,以Set集合的形式返回
Set<String> set = map.keySet();
//获取键集合的迭代器
Iterator<String> iterator = set.iterator();
//一次遍历集合根据键获取值
while(iterator.hasNext()){
String next = iterator.next();
System.out.println(map.get(next));
}
}
常用类的比较(简单的分析,以后会对其源码进行分析):
HashMap<K,V>:存储数据采用的数组+链表+红黑树,元素的存取顺序不能保证一致,由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
LinkedHashMap<K,V>:HashMap下的子类,存储数据采用的哈希表结构+链表结构,通过链表结构可以保证元素存取顺序一致;通过哈希表结构可以保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法
集合工具类Collections的常用方法,其方法为类方法:
sort(集合对象);//将集合中的元素进行排序
shuffle(集合对象);//将集合中的元素位置打乱
集合体系中的接口、抽象类、实现类比较多:
接口:用来明确所有集合中该具有的功能,相当于在定义集合功能标准
抽象类:把多个集合中功能实现方式相同的方法,抽取到抽象类实现,具体集合不再编写
实现类:根据自身的数据存储结构方式,对接口中的功能方法进行不同方式的实现
小结:
Map集合元素是成对出现的,K-V形式,键不可以重复,值可以重复
遍历map的方式:
1.根据键找值:先获取键的集合,在进行获取键对应的值
map.keySet();map.get(key)
2.根据键值对对象找键和值:map.entrySet();entry.getKey();entry.getValue();
3.java的IO:
3.1 javaIO的体系结构
3.2 File类:
public static void main(String[] args) {
//方式一
String pathName = "E:\\file\\Hello.java";
File file = new File(pathName);
//方式二
File file2 = new File("E:\\file","Hello.java");
//方式三
File file3 = new File("FileDemo.java");
//获取绝对路径
String absolutePath = file3.getAbsolutePath();
File absoluteFile = file3.getAbsoluteFile();
//获取全部文件大小
long length = file3.length();
//获取文件路径,File封装什么获取什么
String path = file3.getPath();
String name = file3.getName();
}
3.2.1文件和文件夹的创建和删除
private static void testFile() throws IOException {
//文件的创建
File file = new File("E:\\file\\Hello.txt");
//创建文件,文件不存在时创建文件并返回true,文件存在时,返回false
boolean b1 = file.createNewFile();
System.out.println(b1);
//文件的删除
boolean b2 = file.delete();
System.out.println(b2);
//创建目录、删除、判断
File dir = new File("E:\\file\\aaa");
boolean b3 = dir.mkdir();
System.out.println(b3);
//目录的删除
boolean b4 = dir.delete();
System.out.println(b4);
//文件或目录的判断
File file2 = new File("E:\\file\\kkk");
System.out.println(file2.isFile());//是否为文件
System.out.println(file2.isDirectory());//是否问目录
}
3.2.2 获取文件夹下的文件
private static void getAllFile() {
//指定一个目录
File dir = new File("E:\\file\\aaa");
//获取目录下的所有的文件以及文件夹的名称
String[] names = dir.list();
for(String name : names){
System.out.println(name);
}
//获取当前目录下当前文件以及文件对象
File[] files = dir.listFiles();
for(File file : files){
System.out.println(file);
}
}
3.2.3 按条件获取文件对象
自定义文件过滤
/**
* 过滤器类,该过滤器过滤选择后缀为txt的文件
*/
public class MyFileFilter implements FilenameFilter {
@Override
public boolean accept(File file, String s) {
return s.endsWith(".txt");
}
}
/**
* 过滤器类,该过滤器过滤选择所有的文件,不选择文件夹
*/
public class MyFileFilter implements FileFilter {
@Override
public boolean accept(File file) {
return file.isFile();
}
}
获取指定后缀的文件对象
private static void fileFilter() {
//按条件获取文件
File dir = new File("E:\\file\\aaa");
//获取当前目录下当前文件以及文件对象
File[] files = dir.listFiles(new MyFileFilter());
for(File file : files){
System.out.println(file);
}
}
递归获取文件夹中的文件(包含子文件夹下的文件)
public static void main(String[] args) throws IOException {
File dir = new File("E:\\file\\aaa");
getFileAll(dir);
}
private static void getFileAll(File dir) {
File[] files = dir.listFiles();
for(File f :files){
if(f.isDirectory()){
getFileAll(f);
}else{
System.out.println(f);
}
}
}
3.3 字节流
上面的File类主要是操作文件、文件夹,对于文件中并没有写数据或读取数据。
3.3.1 字节输出流
OutputStream:表示所有输出类的超类,操作的数据为字节,定义了字节输出流的基本共性功能方法。将数据从程序写入磁盘中
close();关闭此输出流并释放与此流相关的所有系统资源
flush();刷新此输出流强制写出所有缓冲的输出字节
write(byte[] b);将数组长度的字节从指定的byte数组写入此输出流
write(byte[] b , int off,int len);将指定byte数组中从偏移量off开始的len个字节写入此输出流
write(int b);将指定的字节写入此输出流
文件输出流FileOutputStream,是OutputStream的子类
private static void writeData() throws IOException {
//创建指定File对象表示的文件中写入数据的文件输出流,这样写会将原有的文件进行覆盖掉,如果想进行续写,构造函数中传入一个布尔类型的变量,设置值为true
FileOutputStream out = new FileOutputStream(new File("E:\\file\\aaa\\a.txt"));
byte[] data = "我一生中最爱的人,嫁给了别人,我已经做好了漂泊一生的打算".getBytes();
//文件输出流将数据写入指定的文件中
out.write(data);
//关闭流
out.close();
}
3.3.2 字节输入流
InputStream:将数据从磁盘文件读取到程序中。
read();从输入流读取数据的下一个字节
read(byte[] b);从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中
文件输入流FileInputStream,可以获取文件中的数据,并将其放入一个缓冲区数组
//方式1,不使用缓冲区
public static void main(String[] args) throws IOException {
//writeData();其存在中英文乱码转换的问题,目前还没想到单个字符如何解决编码
File file = new File("E:\\file\\aaa\\a.txt");
FileInputStream fins = new FileInputStream(file);
int ch = 0;
while((ch=fins.read())!=-1){
System.out.print((char)ch);
}
fins.close();
}
//使用缓冲区接受字节对象
public static void main(String[] args) throws IOException {
File file = new File("E:\\file\\aaa\\a.txt");
//创建字节输入流,从指定的文件对象中获取字节信息
FileInputStream fin = new FileInputStream(file);
byte[] data = new byte[1024];
int len = 0;
//读取一个字节,如果为空字符返回值为-1
while((len=fin.read( data))!=-1){
System.out.println(new String(data,0,len));
}
fin.close();
}
简单的文件复制操作:
public static void main(String[] args) {
//写文件的参数来自读取的文件中
writeFile(readFile());
}
//写文件,将传入的数据写入另一个文件
public static void writeFile(String message){
File file = null;
FileOutputStream fout = null;
try {
file = new File("E:\\file\\aaa\\copy1.txt");
fout = new FileOutputStream(file);
fout.write(message.getBytes());
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
fout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//读取文件中的数据并返回
public static String readFile(){
File file = null;
FileInputStream fileInputStream =null;
StringBuilder sb = null;
try {
file = new File("E:\\file\\aaa\\copy.txt");
fileInputStream = new FileInputStream(file);
byte[] data = new byte[1024];
int len = 0;
sb = new StringBuilder();
while((len =fileInputStream.read(data))!=-1){
System.out.println(new String(data,0,len));
sb.append(new String(data,0,len));
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sb.toString();
}
3.4 字符流
回顾字节流读写文件
//使用字节流读取文件中的中文字符
public static void readCNText() throws IOException {
FileInputStream fis = new FileInputStream("E:\\file\\aaa\\ch.txt");
int ch = 0;
while((ch = fis.read())!=-1){
System.out.println(ch);
}
fis.close();
}
//使用字节流写文件
public static void writeText()throws IOException{
FileOutputStream fos= new FileOutputStream("E:\\file\\aaa\\ch.txt");
fos.write("中文是世界上最牛的语言,我喜欢中文".getBytes());
fos.close();
}
字节流读取的中文信息时一些字符的编码。
字符的编码表:
ascii编码表:a
z: 97122 AZ:6590 0-9:4~57iso-8859-1:拉丁编码表
GB2312:简体中文编码表
unicode:国标码,使用两个字节存储
UTF-8基于unicode,一个字节就可以存储数据,不要用两个字节存储
3.4.1 字符输入流Reader
读取字符流的抽象超类
read() 读取单个字符
read(char[] cbuf) 将字符读入数组
1.FileReader类:是InputStreamReader的子类,用来读取字符流。
FileReader(File file):在给定文件中读取数据的file的情况下创建一个新FileReader
FileReader(String fileName):在给定文件名的情况下创建一个FileReader对象
简单读取中文的实例:
public static void readFileReader() throws IOException {
FileReader fr = new FileReader("E:\\file\\aaa\\ch.txt");
int ch = 0;
while((ch = fr.read())!=-1){
//System.out.print(ch);
System.out.print((char)ch);
}
fr.close();
}
3.4.2 字符输出流Writer
想文件写入字符
write(char[] cbuf);写入字符数组
write(char[] buf,int off,int len);写入字符数组的一部分
write(int c);写入单个字符
write(String str);写入字符串
write(String str,int off,int len);写入字符串一部分
FileWrite类写入字符流
构造函数:
FileWrite(File file);根据给定Field对象构造一个fieldWriter对象
FileWrite(File file , boolean append);
FileWrite(String fileName);根据给定的文件名构造一个FileWriter对象
FileWrite(String fileName,boolean appeand);
字符流将中文写入文件
//使用字符流写文件
public static void writeFileWriter() throws IOException {
FileWriter fw = new FileWriter("E:\\file\\aaa\\fw.txt");
fw.write("你好,中国将越来越好,计算机将越来越深入人们的生活");
fw.flush();
fw.close();
}
flush()与close()
flush()将流中的缓冲区缓冲的数据刷新到目的地中,刷新后,流还可以继续使用
close()关闭资源,关闭之前将缓冲区的数据刷新到目的地,再关闭流
小练习:使用字符流进行文件复制
public static void readWriteFile() throws IOException {
//创建文件字符输入流
FileReader fr = new FileReader("E:\\file\\aaa\\fw.txt");
//创建文件字符输出流
FileWriter fw = new FileWriter("E:\\file\\aaa\\copy.txt");
//读取文件字符放入缓冲区
char[] buffer = new char[1024];
int ch = 0;
while((ch = fr.read(buffer))!=-1){
fw.write(buffer,0,ch);
}
fw.close();
fr.close();
}
3.5 转换流
OutputStreamWriter是字符流通向字节流的桥梁,可用于指定的字符编码表,将要写入流中的字符编码成字节,将字符串按照指定的编码转成字节,再使用字节流将字节输出
3.5.1 输出转换流
OutputStreamWriter将字符流转换成字节流,使用指定的编码表将要写入输入流的字符编码成字节作用是将字符串按照指定的编码表转换成字节,使用字节流将字节输出
public static void writeCh() throws IOException {
//创建文件相关的字节输出流对象
FileOutputStream fos = new FileOutputStream("E:\\file\\aaa\\cn8.txt");
//创建字符转换成字节的转换流对象,并指定编码
OutputStreamWriter osw = new OutputStreamWriter(fos,"utf-8");
//调用转换流,把文字写出去,其实是写到转换流的缓冲区中
osw.write("中文越来越国际化");
osw.close();
}
3.5.2 输入转换流
InputStreamReader用于将字节流转换成字符流
public static void readCh() throws IOException{
//创建相关的字节输入流对象
FileInputStream fin = new FileInputStream("E:\\file\\aaa\\cn8.txt");
//创建字符转换成字节的转换流对象,并指定编码
InputStreamReader isr = new InputStreamReader(fin,"utf-8");
//使用转换流去读取字节流中的字节
int ch = 0;
while((ch = isr.read())!=-1){
System.out.print((char)ch);
}
isr.close();
}
字符类关系:
OutputStreamWriter
|—FileWriter
InputStreamReader
|---FileReader
子类和父类的功能的区别:
父类是字符和字节之间的桥梁,也可以称之为字符转换流,原理是字节流+编码表
子类便于操作字符文件,当操作字符文件时,默认编码表不可以使用父类,直接使用子类简化程序。
3.6 缓冲流
java中使用缓冲流操作文件时提高效率
3.6.1 字节缓冲流
1.字节输入缓冲流BufferedOutputStream:构造函数
public BufferedOutputStream(OutputStream out)创建一个新的缓冲输出流,将数据写入指定的底层输出流
2.字节输出缓冲流BufferedInputStream:构造函数
public BufferedInputStream(InputStream in)创建一个新的缓冲输出流,将数据写入指定的底层输出流
public static void bufferFile()throws IOException{
//写入文件的流对象
FileOutputStream out = new FileOutputStream("E:\\file\\aaa\\buf.txt");
//缓冲流对其进行包装
BufferedOutputStream bufout = new BufferedOutputStream(out);
//进行输出
bufout.write("大千世界,竟没有我的容身之所".getBytes());
bufout.close();
//读取文件
//创建流对象,不能处理中文,只是图片等文件的字节流
FileInputStream fin = new FileInputStream("E:\\file\\aaa\\buf.txt");
//将基本的流对象进行包装
BufferedInputStream bfin = new BufferedInputStream(fin);
//读取数据
int ch = -1;
while((ch=bfin.read())!=-1){
System.out.println((char)ch);
}
bfin.close();
}
3.6.2 字符缓冲流
1.字符缓冲输入流
BufferedReader,读取文本行,缓冲各个字符,从而实现字符、数组和行的高效读取,到达文末时返回null
2.字符缓冲输出流
BufferedWriter,将文本写入字符输出流,缓冲各个字符,从而提供翻个字符、数组和字符串的高效写入
public static void bufferRWtest()throws IOException{
//创建字符输出流
FileWriter fw = new FileWriter("E:\\file\\aaa\\bufch.txt");
//创建缓冲流对象,将基本的输出流进行包装
BufferedWriter bw = new BufferedWriter(fw);
//写数据
String str = "当幸福恋人寄来红色分享喜悦,是慌乱占据了心扉";
bw.write(str,0,str.length());
bw.close();
//创建字符输入流
FileReader fr = new FileReader("E:\\file\\aaa\\bufch.txt");
//创建缓冲对象包装字符输入流
BufferedReader bf = new BufferedReader(fr);
//读取数据
String line = null;
while((line=bf.readLine())!=null){
System.out.println(line);
}
bf.close();
}
使用流需要注意的有:
1.操作的数据时输入还是输出:
输入:InputStream /Reader
输出:OutputStream/Writer
2.操作的是文本还是字节数据
字节:OutputStream
文本:Writer
3.明确数据所在的具体设备
硬盘:文件File
内存:数组或者字符串
键盘:System.in/System.out
网络:Socket
4.是否需要转换流或者是缓冲对象
3.7 Properties类
Properties类表示了一个持久的属性集,用于加载或写入流
//该类可以使用Map的方法
public
class Properties extends Hashtable<Object,Object>
//传入的键值为字符串类型
public synchronized Object setProperty(String key, String value) {
return put(key, value);
}
特点:
1.该类是HashTable的子类,map集合中的方法都可以用
2.该集合没有泛型,键值都是字符串
3.它是一个可以持久化的属性集。键值可以存储到集合中,也可以存储到持久化的磁盘,键值的来源也可以是持久化的设备。
方法
load(InputStream instream) 输入流中读取属性列表
load(Reader reader) 输入的字符流中读取属性列表
store(OutputStream out,String comments) 属性列表写入输出流
store(Writer writer,String comments) 属性列表写入输出字符
向Properties对象中放数据:
private static void testProperties() {
// 创建集合对象,可以看做hashtable
Properties pro = new Properties();
// 添加元素到集合中
pro.setProperty("A", "Z");
pro.setProperty("B", "Y");
pro.setProperty("C", "X");
Set<String> keys = pro.stringPropertyNames();
for (String key : keys) {
String value = pro.getProperty(key);
System.out.println("key = " + key + " value = " + value);
}
}
使用Properties和输出流FileWriter将属性表写入文件中
public static void main(String[] args) {
// 创建集合对象,可以看做hashtable
Properties pro = new Properties();
// 添加元素到集合中
pro.setProperty("jdbcDriver", "com.jdbc.mysql.Driver");
pro.setProperty("jdbcurl", "http://localhost:3306/table?useUnicode=true&charsetEncoding=UTF8");
pro.setProperty("jdbcUser", "root");
pro.setProperty("jdbcPassword", "123");
System.out.println(pro.get("jdbcUser"));
FileWriter fw = null;
try {
//自己文件所在的位置
//创建流
fw = new FileWriter("src/com/xingzai/stream/db.properties");
//将属性元素写入流中
pro.store(fw, "save data");
fw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
利用Properties和文件输入流读取配置文件的元素
public static void main(String[] args) {
// writeFile();
Properties pro = new Properties();
FileReader read = null;
try {
//配置文件位置
read = new FileReader("src/com/xingzai/stream/db.properties");
//加载配置文件
pro.load(read);
System.out.println(pro.get("jdbcurl"));
read.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
3.8 序列化流与反序列化
用于将对象写入流中的操作流称为序列化流
用于从流中获取对象的操作流称为反序列化流
对象序列化流ObjectOutputStream将java对象的基本数据类型写入OutputStream,也可以使用OutputStream读取对象。
ObjectOutputStream(OutpurStream out)
writeObject(Object obj)
// 对象序列化
private static void serializeObj() throws FileNotFoundException, IOException {
// 文件输出流
FileOutputStream out = new FileOutputStream("src/com/xingzai/stream/obj.object");
// 给操作文件对象加入写入对象功能
ObjectOutputStream oos = new ObjectOutputStream(out);
// 3.调用写入对象的方法
oos.writeObject(new Person("laji", 25));
// 关闭资源
oos.close();
}
// 对象反序列化
private static void reverseparalizeObj() throws IOException, FileNotFoundException, ClassNotFoundException {
// 对象输入流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("src/com/xingzai/stream/obj.object"));
// 获取对象实例
Person per = (Person) ois.readObject();
System.out.println(per.toString());
}
注:一个对象想要被序列化,则个对象所属的类必须实现Serializable接口。
序列化对象如果发生InvalidClassException异常,可能的原因是:
类的序列号版本号从流中读取的类描述符的版本号不一致
该类包含未知数据类型
该类没有可访问的构造函数
一般序列化对象是,会加入版本号:
private static final long serialVersionUID = 1L;
transient关键字,类中被该关键字修饰的变量不会被序列化。
3.9 打印语句
打印流添加数据的功能,使他们能够方便地打印各种数据值,一般分为字节打印流(PrntStream)和字符打印流(PrintWriter)
字符打印流打印数据到文件
public static void main(String[] args) throws IOException {
// 文件字符输出流
// PrintWriter pw = new PrintWriter(new FileWriter("src/com/xingzai/stream/print.txt"));
PrintWriter pw = new PrintWriter("src/com/xingzai/stream/print1.txt");
// 写数据
for (int i = 0; i < 5; i++) {
pw.println("你好");
}
// 关闭流
pw.close();
}
构造函数:
第一个参数为一个字符输出流,第二个参数为刷新功能
new PrintWriter(Writer out,boolean autoFlush);
参数为字符输出流
new PrintWriter(Writer out);
第一个参数为字节输出流,第二个参数为自动刷新功能
new PrintWriter(OutputStream out , boolean autoFlush)
3.10 第三方IO包Commons-IO
//获取文件扩展名
String name = FilenameUtils.getExtension("src/com/xingzai/stream/print1.txt");
//判断文件扩展名和指定元素是否相同
boolean name = FilenameUtils.isExtension("src/com/xingzai/stream/print1.txt","txt");
//获取文件名
String name = FilenameUtils.getName("src/com/xingzai/stream/print1.txt");
FileUtils类:
该类提供文件的各个操作(文件移动、读取文件、检查文件是否存在等)
使用工具类测试文件复制
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
copy3File();
long end = System.currentTimeMillis();
System.out.println(end - start);
}
// 方式一:复制文件操作
public static void copyFile() throws IOException {
String content = FileUtils.readFileToString(new File("src/com/xingzai/stream/print1.txt"));
FileUtils.writeStringToFile(new File("src/com/xingzai/stream/print2.txt"), content);
}
// 方式二:复制文件操作
public static void copy2File() throws IOException {
FileUtils.copyFile(new File("src/com/xingzai/stream/print2.txt"),
new File("src/com/xingzai/stream/print3.txt"));
}
// 方式三:复制文件
public static void copy3File() throws IOException {
// 使用缓冲输入流和文件输入流将文件读取
BufferedInputStream in = new BufferedInputStream(new FileInputStream("src/com/xingzai/stream/print1.txt"));
// 使用缓冲输出流和文件输出流将文件输出
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("src/com/xingzai/stream/print4.txt"));
// 开始读取文件,使用一个缓冲数组
byte[] buffer = new byte[1024];
int len = -1;
while ((len = in.read()) != -1) {
out.write(buffer);
}
// 关闭输入输出流
in.close();
out.close();
}
使用缓冲流的方法效率最高
文件夹的复制:
// 文件夹复制
public static void copyDir() throws IOException {
FileUtils.copyDirectory(new File("src/com/itxing/log4j"), new File("src/com/xingzai/log4j"));
}
4.注解与反射
注解与反射相当重要,在框架中许多地方都能够用到。
4.1 注解
注解是为了对程序做一些解释,主要是为了程序能够理解,java中内置的注解有:
@Override 表示一个方法声明,子类重写父类中的方法声明
@Deprecated 定义一个方法过期的注解
@SuppressWarings 警告压制注解
元注解:java中有4个标准的meta-annotion类型,用来对其他注解的类型说明:
@Target : 描述注解的使用范围
@Retention :表示保存注释的级别(SOURCE<CLASS<RUNTIME)
@Documented :说明该注解在javadoc中
@Inherited:说明子类可以继承父类中的该注解
自定义注解:
@Target(ElementType.TYPE)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Documented
//自定义注解
@interface MyAnno {
//自定义注解的参数
String name() default "zhujie";
}
4.2反射
静态语言和动态语言:
动态语言:是一类在运行时可以改变其结构的语言(Object-c、C#、Javascript、PHP、Python)
静态语言:运行时结构不可以改变的语言就是静态语言,如:java、c、c++
java不是动态语言,但是java有一定的动态性,利用反射机制可以获取类似于动态语言的特性
Relection(反射)可以在程序执行期间借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
Class c = Class.forName("java.lang.String");
反射:通过实例化对象——–getClass方法———-得到完整的包和类
反射的优缺点:
优点:可以实现动态创建对象和编译,体现出很大的灵活性
缺点:对性能有影响,使用反射基本上是一种解释操作,操作比较慢
创建一个实体类:
class User {
private String name;
private int id;
private int age;
public User() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + "]";
}
}
获取实体类的Class对象
public static void main(String[] args) throws ClassNotFoundException {
Class clazz1 = Class.forName("com.itxing.annotion.User");
Class clazz2 = Class.forName("com.itxing.annotion.User");
// 一个类在内存中只有一个Class对象
System.out.println(clazz1 == clazz2);
\}
Class类的常用方法
方法名 | 功能说明 |
---|---|
static ClassforName(String name) | 返回指定类名的Class对象 |
Object newInstance() | 调用缺省的构造函数,获取Class |
getName() | 返回Class对象所表示的实体名称 |
Class getSuperClass | 返回当前Class对象的父类对象 |
Class[] getinterfaces() | 返回当前Class对象的接口 |
ClassLoader getClassLoader() | 返回类加载器 |
Constructor[] getConstructors() | 返回一个包含某些Constructor对象的数组 |
Method getMethed(String name,Class… T) | 返回一个Method对象,此对象的形参类型为paramType |
Field[] getDeclaredFields() | 返回Field对象的一个数组 |
// 获取对象的三种方式
// 方式一
User user = new User();
Class u1 = user.getClass();
// 方式二
Class u2 = Class.forName("com.itxing.annotion.User");
// 方式三
Class u3 = User.class;
//基本内置类型的包装类都有一个Type属性(基本类型的第四种方式)
Class type = Integer.TYPE;
//也可以由子类获取父类的Class对象
反射与java内存空间有关
类加载与ClassLoader:
1.加载:将Class文件字节码内容加载到内存中,并将这些静态数据换成方法区的运行时数据结构,然后生成一个代表这个类的Class对象.
2.连接:将java类的二进制代码合并到JVM的运行状态之中的过程
- 验证:确保加载类信息符合JVM规范,没有安全方面的问题
- 准备:正为类变量(static)分配内存并设置类变量默认初始化值的阶段,这些内存都将在方法区中进行分配
- 解析:虚拟机常量池的符号引用替换为直接引用的过程
3.初始化:
- 执行类构造器
()方法的过程,类构造器 ()方法是由编译器自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。 - 当初始化一个类时,父类没初始化时先初始化父类
- 虚拟机保证一个类的
方法在多线程环境中被正确加锁和同步
类的类加载器
类加载器是将类装载到内存中,JVM规定了三种类加载器:
引导类加载器:Bootstap Classloader,使用c++编写的,是JVM自带的类加载器,负责java平台的核心库,用来装载核心类库
扩展类加载器:负责jre/lib/ext目录下的jar包或java.ext.dirs指定目录下的jar包
系统类加载器负责java -classpath:或-D java.class.path所指定的目录下的类与jar包,最常用的类加载器
5.多线程:
5.1多线程的体系结构图
5.2 多线程中的基本概念
1.进程与线程
进程:指的是运行中的程序,确切的说,当一个程序进行内存运行,变成了一个进程,具有一定的独立性
线程:线程是进程的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中有多个线程,该应用被称之为多线程程序
进程是系统调度的基本单位,线程是系统执行的基本单位
2.程序运行的原理
分时调度:所有线程轮流使用CPU,平均分配每个线程cpu的时间
抢占式调度:优先级高的线程使用cpu,如果线程的优先级相同,那么会随机选择(java中使用的为抢占式调度)
3.操作系统中的作业/进程调度算法
a.先来先服务:从后备队列中选择最先入队的一个或几个作业,将其调入内存
缺点是:效率低,不利于短作业
b.短作业优先:从后备队列中选择一个或若干个运行时间最短的作业,调入内存
缺点:对长作业不利,不能考虑时间的紧迫性
c.优先级调度算法:从后备队列中选择优先级最高的作业,调入内存
非剥夺式优先级调度:有紧迫任务也要入队
剥夺式优先级调度:有紧迫任务,暂停当前任务,释放自身资源
d.高响应比 : R = (等待时间+要求服务时间)/(要求服务时间)
等待时间相同时,服务时间短先服务,利于短作业
服务时间相同时,取决于等待时间,先来先服务
e.时间片轮转算法:(分时系统)先后次序组成队列,每次执行第一个进程,只能运行一个时间片
f.多级反馈队列调度算法:优先级和时间片轮转综合,优先级越高时间片越小,多个就绪队列,为各个队列赋予不同的优先级,不同的队列分配不同的时间片
4.并发与并行
并发:并发是指两个或多个事件在同一时间间隔内发生
并行:并行是指两个或多个事件在同一时刻发生
5.3 线程的状态
线程的状态转换图:
5.4 java中的线程创建
方式一:继承Thread类(缺点是不能够再继承其他的类)
构造函数:
Thread()分配新的Thread对象
Thread(String name)分配新的Thread对象,将指定的name作为其线程的名称
常用方法:
start();使用该方法使线程准备执行
run():线程要执行的操作
sleep(long millis):指定的毫秒数内让当前正在执行的线程休眠
Thread.currentThread():获取当前正在执行的线程对象的引用
Thread.currentThread().getName():获取当前线程的名字
public class TestThreadDemo {
public static void main(String[] args) {
// 创建线程并启动
new MyThread("my ").start();
// 主线程的其他程序
for (int i = 0; i < 10; i++) {
System.out.println("main " + i);
}
}
}
//继承Thread类
class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName() + i);
}
}
}
run方法与start方法的区别?
线程对象调用run方法不开启线程,仅是方法调用,线程对象调用start开启线程,并让jvm调用run方法在开启的线程中执行。
多线程执行时,在栈内存中,每个执行的线程都有一片属于自己的栈内存空间,进行方法的压站和弹栈,当执行线程任务结束,线程自动在栈内存中释放
方式二:实现Runnable接口(可以继承其他的类,但是没有返回值)
Thread(Runnable target):分配新的Theread对象,以便将target作为其运行对象
Thread(Runnable target,String name):分配新的Thread对象,以便将target作为其运行对象,将name指定为其名称
步骤:
1.创建实现类实现Runnable接口
2.覆盖接口中的run方法
3.创建Thread类对象
4.实现类作为参数传递给Thread的构造函数中
5.调用Thread对象的start方法
public class ThreadDemo02 {
public static void main(String[] args) {
MyRunThread r = new MyRunThread();
//创建Thread,传递接口实现类参数,启动线程
new Thread(r, "firstR").start();
//主方法的线程
for (int i = 0; i < 10; i++) {
System.out.println("main " + i);
}
}
}
//创建实现类,实现Runnable接口
//覆盖接口方法
class MyRunThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
使用匿名内部类的方式:
new Thread() {
public void run() {
for (int x = 0; x < 40; x++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}.start();
使用java8的lambda表达式写法:
// lambda表达式的写法
new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}, "twoR").start();
方式三:实现Callable接口,并使用FutureTask类进行线程的创建(带有返回值)
常规使用:
public class ThreadDemo {
public static void main(String[] args) {
// 使用FutureTask对象作为参数,创建并开启相信线程
FutureTask future = new FutureTask<Integer>(new CallThread());
new Thread(future, "task").start();
try {
// 返回值用sum接受
Integer sum = (Integer) future.get();
System.out.println(sum);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class CallThread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i < 10; i++) {
sum += i;
}
return sum;
}
}
lambda写法:
//lambda创建fu对象,实现Callable接口
FutureTask fu = new FutureTask(() -> {
int sum = 0;
for (int i = 0; i < 10; i++) {
sum += i;
}
return "aa" + sum;
});
//创建线程对象,开启线程
new Thread(fu, "newFu").start();
try {
//获取返回值并打印
System.out.println(fu.get());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
5.5线程池
线程池概念和好处
线程池,是一个容纳多个线程的容器,使得线程可以反复的使用,省去了频繁创建线程对象的操作,无需反复创建而消耗过多的资源
好处就是避免了创建与销毁线程所带来的系统的开销
常用类及常用方法:
Executors:线程池创建工厂类
Executors.newFixedThreadPool(int n)
ExecutorService:线程池类
submit(Runnable task):获取线程池中的某一个线程对象,并执行
Future接口:用来记录线程执行完成后产生的结果,线程池创建与使用
使用线程池中线程对象的步骤:
创建线程池对象—–创建Runnable接口子类对象—-提交Runnable接口子类对象——-关闭线程池
实例一:Runnable接口
private static void runnable() {
// 创建一个数量为两个线程的线程池
ExecutorService service = Executors.newFixedThreadPool(2);
// 从线程池获取线程并执行任务
service.submit(() -> {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}, "aaa");
// 关闭线程池
service.shutdown();
}
实例二:Callable接口,使用Future接受返回值
//自定义实现类以及任务
class MyCall implements Callable<Integer> {
public MyCall() {
}
int a;
int b;
public MyCall(int a, int b) {
this.a = a;
this.b = b;
}
@Override
public Integer call() throws Exception {
return a + b;
}
}
//创建线程池,执行任务,获取返回值
public static void main(String[] args) {
// 创建一个数量为两个线程的线程池
ExecutorService service = Executors.newFixedThreadPool(2);
// 自定义的Callable子类
MyCall call = new MyCall(5, 9);
// 从线程池获取线程并执行任务
Future<Integer> fu = service.submit(call);
try {
System.out.println(fu.get());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
service.shutdown();
}
6. 网络编程:
(七月中旬才复习到网络编程,由于学校的一些事情,耽误了不少时间)
6.1 网络编程介绍
计算机网络:把分布在不同地理区域的计算机与专门的外部设备用通信线路互联互联成一个规模大、功能强的网络系统,从而使众多的计算机可以方便地互相传递信息、共享硬件、软件、数据信息等资源。
网络编程的目的是直接或间接地通过网络协议与其他计算机实现数据交换
网络编程中的两个问题:
1.如何准确的定位网络上的一台或多台主机,以及主机的应用?
2.找到主机后如何高效可靠的传输数据?
通信双方的地址表示:ip地址、端口号
通信数据传输指定一定的规则,称之为通信协议,对速率、传输代码、代码结构、传输控制步骤、出错控制等指定标准。
OSI参考模型(7层) | TCP/IP参考模型(4层) | TCP/IP参考模型对应协议 |
---|---|---|
应用层 | 应用层 | |
表示层 | 应用层 | HTTP、FTP、Telenet、DNS… |
会话层 | 应用层 | |
传输层 | 传输层 | TCP、UDP |
网络层 | 网络层 | IP、ICMP、ARP |
数据链路层 | 物理+数据链路层 | Link |
物理层 | 物理+数据链路层 |
IP地址分类1:
IPV4: 4个字节组成,大致有42亿,30亿在北美,亚洲有4亿,以点分十进制表示
Ipv6:128位16个字节,写成8个无符号整数,每个整数用四个十六进制位表示,数之间用冒号:分开(3ffe:3201:1401:5421:c8ff:fe4d:db39:1984)
IP地址分类2:
公网地址:万维网中使用
私有地址:局域网中使用,192.168.开头等
ip地址是互联网上的唯一标示,在java中使用InetAddress类表示
// 根据ip解析ip
InetAddress net1 = InetAddress.getByName("192.168.10.14");
// 根据域名解析ip
InetAddress net2 = InetAddress.getByName("www.baidu.com");
// 获取本地地址
InetAddress net4 = InetAddress.getLocalHost();
// getHostName(String host)
// getHostAddress(String host)
System.out.println(net4);
端口号:标识正在计算机上运行的进程
不同的进行有不同的端口号,是一个0~65535的一个16位整数。
端口分类:
公认端口:0~1023,预先定义的服务通信占用(Http是80,FTP是21,Telnet占用23)
注册端口:1024~49151,分配给用户进程或应用程序
动态/私有端口:49152~65535
ip地址与端口号组合在一起称之为Socket,网络编程又称为Socket编程。
传输层协议中使用的有TCP(传输控制协议)与UDP(用户数据报协议)协议。
TCP/IP协议族:传输控制协议(TCP)和网络互联协议IP而得名,实际上是一组协议,包括多个具有不同功能且互为关联的协议。IP协议是网络层的主要协议,支持网络间互连的数据通信。TCP/IP协议模型从更实用的角度出发,形成了高效的四层体系结构,即物理链路层,IP层,传输层,应用层。
6.2 TCP协议
1.使用TCP协议之前,须先建立TCP连接,形成传输数据通道
2.传输前,采用”三次握手”方式,点对点通信,可靠的
3.TCP协议先进性通信的两个应用进程:客户端、服务器
4.在连接中可进行大数据量的传输
5.传输完毕,需释放已建立的连接,效率低
三次挥手:
四次挥手:
TCP实例:
客户端程序:
@Test
public void client() {
Socket socket = null;
OutputStream os = null;
FileInputStream fin = null;
InputStream in = null;
ByteArrayOutputStream arrStream = null;
try {
// 创建一个socket,包括ip地址和端口号
socket = new Socket(InetAddress.getByName("127.0.0.1"), 9098);
// 获取socket输出流
os = socket.getOutputStream();
// 获取文件输入流,用于传输文件
fin = new FileInputStream(new File("src\\main\\java\\3.jpg"));
// 创建一个缓冲数组
byte[] data = new byte[1024];
// 循环将数据写入数组中
int len;
while ((len = fin.read(data)) != -1) {
os.write(data, 0, len);
}
// 声明传输完毕
socket.shutdownOutput();
// 发送完数据等待服务器端的消息,获取socket输入流
in = socket.getInputStream();
// 创建一个字节数组输出流。用于记录接受的数据
arrStream = new ByteArrayOutputStream();
byte[] buffer2 = new byte[10];
int len2;
while ((len2 = in.read(buffer2)) != -1) {
arrStream.write(buffer2, 0, len2);
}
// 输出服务器回复的消息
System.out.println(arrStream.toString());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
// 关闭所有资源
if (fin != null)
try {
fin.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (os != null)
try {
os.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (in != null) {
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (arrStream != null) {
try {
arrStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
服务器程序:
@Test
public void server() {
ServerSocket ss = null;
Socket socket = null;
InputStream inputStream = null;
FileOutputStream fos = null;
OutputStream os = null;
try {
//创建socket对象并监听9098端口
ss = new ServerSocket(9098);
socket = ss.accept();
//获取socket输入流
inputStream = socket.getInputStream();
//获取文件输出流,将客户端传输的文件写入新的路径
fos = new FileOutputStream(new File("buty.jpg"));
byte[] buffer = new byte[1024];
int len;
while ((len = inputStream.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
// 给客户端回消息
os = socket.getOutputStream();
os.write("是朱茵".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭资源
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (ss != null) {
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
6.3 UDP协议
1.将数据、源、目的封装成数据,不需要建立连接
2.每个数据报的大小限制在64K内
3.发送不管对方是否准备好,接收方收到也不确认,不可靠
4.可以广播发送
5.发送数据结束时无需释放资源,开销小,速度快
UDP客户端
@Test
public void sender() {
try {
//创建DatagramSocket socket对象
DatagramSocket socket = new DatagramSocket();
String str = "udp发送数据";
byte[] buffer = str.getBytes();
//发送数据的地址
InetAddress inet = InetAddress.getLocalHost();
DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length, inet, 9989);
//发送数据
socket.send(packet);
//应该在finally中关闭流对象
socket.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
UDP服务器端
@Test
public void receiver() {
try {
//创建DatagramSocket对象并监听端口
DatagramSocket socket = new DatagramSocket(9989);
byte[] buffer = new byte[200];
DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
//服务器端接受数据
socket.receive(packet);
System.out.println(new String(packet.getData(), 0, packet.getLength()));
//socket释放,应在finally中
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
6.4 URL编程
(Uniform Resource Locator):同一资源定位符,表示互联网上的某一个资源地址。
URL的基本结构由5本分组成:
<传输协议>://<主机名>:<端口号>/<文件名>#片段名?参数列表
//常用方法
URL url = new URL("http://localhost:8080/examples/beauty.jpg?");
System.out.println(url.getProtocol());// 获取URL的协议名
System.out.println(url.getHost());// 获取URL的主机名
System.out.println(url.getPort());// 获取URL的端口号
System.out.println(url.getPath());// 获取URL的文件路径
System.out.println(url.getQuery());// 获取URL的查询名