Java 核心技术笔记
一. 集合类
- 集合类就像一个容器,相当于一个动态数组,长度可变。属于java.util包。
- 键值对–Map。集合–Set。列表–List。
- 数组用来存放基本类型的数据,集合用来存放对象的引用。
- Collection接口:
方法 | 功能 |
---|---|
add(E e) | 将指定的对象添加到集合中 |
remove(Object obj) | 将指定的对象从集合中移除 |
isEmpty() | 返回布尔值,判断当前集合是否为空 |
iterator() | 返回在此Collection的元素上进行迭代的迭代器。用于遍历集合中的对象 |
size | 返回int值,获取集合中元素的个数 |
package java_2_11;
import java.util.*;
public class Demo
{
public static void main(String[] args)
{
Collection c = new ArrayList();//实例化集合类对象
System.out.println("集合是否为空:"+c.isEmpty());//true表示为空
System.out.println("集合长度:"+c.size());
c.add("你好");
c.add(3.14);
c.add(new Object());
System.out.println("集合是否为空:"+c.isEmpty());//true表示为空
System.out.println("集合长度:"+c.size());
c.remove(3.14);//删除集合中一个值,这句话应该写在迭代器之前,否则会报错
Iterator it = c.iterator();//创建迭代器
while(it.hasNext())//如果迭代器中有下一个值,就把它取出来
{
Object obj = it.next();//获取集合中元素,it.next返回的是一个对象。
System.out.println(obj);
}
}
}
1.1 List集合
特点:元素允许重复,类似与数组
- List继承Collection接口,因此包含Collection中所有方法。
- get(int index)方法:获得指定索引位置的元素。
- set(int index,Object obj)方法:将集合中指定索引位置的对象修改为指定的对象。
List接口常用的实现类有ArrayList和LinkedList:<>表示泛型,可省略。
List<> list = new ArrayList<> ();
List<> list = new LinkedList<> ();
- ArrayList(数组列表)类:实现了可变数组,允许保存所有元素,包括null,并可以根据索引位置对集合进行快速的随机访问;缺点是插入和删除对象速度较慢。
- LinkedList(链表)类:采用链表结构保存对象。优点是便于插入和删除对象。缺点是随机访问集合中的对象效率较慢。
package java_2_11;
import java.util.*;
public class Demo
{
public static void main(String[] args)
{
// List list = new ArrayList();//创建ArrayList集合对象
List list = new LinkedList();//创建LinkedList集合对象,
/*
* LinkedList和ArrayList的方法通用。
* 两者只是在储存方式上不同。
*/
list.add("a");//向集合中添加元素,索引从0开始
list.add("bb");
list.add("ccc");
list.add(2,"你好");//向指定位置添加元素
int len = list.size();
list.set(0, "改变");
// list.remove(2);
list.add("ccc");//set允许有重复的元素
list.add(null);
System.out.println("list长度:"+len);
for(int i=0;i<list.size();i++)
{
System.out.println("索引值为"+i+"的元素为:"+list.get(i));
}
}
}
1.2 Set集合(*)
- Set集合中的对象不按特定的方式排序,只是简单的把对象加入集合中,Set集合中不能包含重复的元素。
- Set集合继承Collection接口。
- Set常用的接口实现类有HashSet类和TreeSet类:
(1)HashSet(散列集合)类不保证Set的迭代顺序,特别是它不保证该顺序恒久不变。此类允许使用null元素。语法:Set<E> set = new HashSet<E>();
。HashSet存储的对象应该重写hashCode()和equals()这两个方法。hashCode()计算要存储对象的存储地址,equals()方法判断要存储对象是否已经存在。同一个哈希地址可以存放多个不同的对象。
(2)TreeSet(树集合)类实现的Set集合在遍历集合时按照自然递增排序,也可以通过比较器对TreeSet类实现的Set集合中的对象进行排序。特点:存放有序。语法:Set<E> set = new TreeSet<E>();
。【注意】:要想实现指定集合的存放顺序,被排序的对象需实现Comparable接口。树集合不能添加null值。
-Set集合中常用的方法有:add()—添加。remove()—删除。contains(Object o)—检查元素是否存在。iterator()—迭代器。 - TreeSet类增加的方法:
方法 | 功能 |
---|---|
first() | 返回此Set中当前第一个(最低)元素 |
last() | 返回此Set中当前最后一个(最高)元素 |
comparator() | 返回对此Set中的元素进行排序的比较器。如果使用自然排序,则返回null |
headSet(E toElement) | 返回一个新的Set集合,新集合是toElement(不包括)之前的所有对象 |
subSet(E fromElement,E toElement) | 返回一个新的Set集合,新集合tfromElement(包括)到toElement(不包括)之间的所有对象 |
tailSet(E fromElement) | 返回一个新的Set集合,新集合是fromElement(包括)之后的所有对象 |
- TreeSet类:
存入TreeSet类实现的Set集合必须实现Comparable接口,该接口中的compareTo(Object o)方法比较此对象和指定对象的顺序。如果该对象小于,等于或大于指定对象,则分别返回负整数(-1),0,正整数(1)。
package java_2_11;
public class Demo1 implements Comparable//Demo1实现Comparable接口
{
int id;
int age;
String name;
/*
* 使用“源码”中的自动生成构造方法,来自动生成构造方法
*/
public Demo1(int id, int age, String name) //构造方法
{
super();
this.id = id;
this.age = age;
this.name = name;
}
@Override
public String toString() //重写toString,用来输出,被迭代器的it.next()调用
{
return "Demo1 [id=" + id + ", age=" + age + ", name=" + name + "]";
}
/*
* 三个返回值,-1,1,0
*/
public int compareTo(Object o) //比较器
{
Demo1 p;
if(o instanceof Demo1)//如果它是Demo1类,
{
p = (Demo1)o;//将o强制转换为Demo1类
}
else
{
return -1;//代表传入参数比我本身要小,排在我的前面
}
int diff = this.age-p.age;//按age排序
if(diff!=0)
{
diff = diff/Math.abs(diff);//差值除以本身的绝对值,得-1或1
}
return diff;
}
}
package java_2_11;
import java.util.*;
public class Demo2
{
public static void main(String[] args) {
Set set = new TreeSet();//实例化TreeSet集合对象
Demo1 p1 = new Demo1(1,18,"小明");//实例化Demo1类的对象
Demo1 p2 = new Demo1(2,5,"大壮");
Demo1 p3 = new Demo1(3,20,"啊强");
set.add(p1);//将对象加入到集合中。
set.add(p2);
set.add(p3);
System.out.println(set.size());
Iterator it = set.iterator();//创建迭代器
while(it.hasNext())//迭代器有下一个值,就执行循环
{
System.out.println(it.next());//自动调用Demo1的toString方法
}
}
}
- HashSet类:
package java_2_11;
public class Person
{
int id;
String name;
public Person(int id, String name) //构造方法
{
super();
this.id = id;
this.name = name;
}
public String toString()//用来自动输出
{
return "Person [id=" + id + ", name=" + name + "]";
}
/*
* 识别方法,判别两个对象
* 这里根据id来判断两个人是不是同一个人
*/
@Override
public int hashCode()//计算存储的哈希地址
{
final int prime = 31;
int result = 1;
result = prime * result + id;
return result;
}
@Override
public boolean equals(Object obj) //使用id作为两个对象的区分
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (id != other.id)
return false;
return true;
}
}
package java_2_11;
import java.util.*;
public class Demo2
{
public static void main(String[] args) {
Set set = new HashSet();//根据哈希值来判断这个对象是否已经存在
Person p1 = new Person(1,"小明");//因为是根据id来判断是不是同一个人
Person p2 = new Person(2,"大壮");//所以,当id相同时,会当做同一个元素来处理
Person p3 = new Person(3,"啊强");
set.add(p1);
set.add(p2);
set.add(p3);
/*
* 当一个元素被添加到集合中后,不要改变使其哈希值能够发生变换的属性。
* 以免删除无效
*/
p2.id = 5;//使其哈希值改变的属性被改变。下面的删除操作无效。
set.remove(p2);//
// set.add(null);
System.out.println(set.size());
Iterator it = set.iterator();
while(it.hasNext())
{
System.out.println(it.next());//根据我们重写的toString方法来输出。
}
}
}
1.3 Map集合
Map集合(键值映射)没有继承Collection接口,其提供的是key到value的映射。Map中不能包含相同的key,每个key只能映射一个value。
(1)Map接口
- Map接口常用方法:
方法 | 功能 |
---|---|
put(K key,V value) | 将键值数据保存到Map中 |
containsKey(Object key) | 查找Map中是否存在某个键 |
containsValue(Object value) | 查找Map中是否存在某个值 |
get(Object key) | 通过键,得到值 |
keySet() | 返回该集合中的所有的key对象形成的Set集合 |
values() | 返回该集合中所有值对象形成的Collection集合 |
(2)Map接口的实现类
- HashMap(散列码键值对)类:速度更快。允许使用null键和null值。不保证映射的顺序。语法:
Map<K,V> m = new HashMap<K,V>();
- TreeMap(树状键值对)类:存放有序。不允许有null。语法:
Map<K,V> m = new TreeMap<K,V>();
。
package java_2_11;
import java.util.*;
public class Demo3
{
public static void main(String[] args)
{
Map m = new HashMap();//创建Map集合对象
// Map m = new TreeMap();
m.put("001", "这是数字001");
m.put("String","这是一个字符串");
m.put("Object",new Object());
m.put("数字", 1234);
/*
* 下面这三种不能别TreeMap保存
*/
m.put(1, "字符串");
m.put(new Object(), new Object());//对象做键,做值都可以
m.put(null, null);
System.out.println("元素个数"+m.size());
Set set = m.keySet();//这个方法将Map中所有的key取出来保存到一个Set中
Iterator it = set.iterator();//创建迭代器
while(it.hasNext())//迭代器中有下一个元素
{
Object obj = it.next();//将它保存为一个Object对象
System.out.println("key: "+obj+" Value: "+m.get(obj));
}
}
}
二. I/O(输入/输出)
- 输入流:数据源(文件,网络等)到程序的流。
- 输出流:程序中的数据输出到目的地(文件,网络等)。
- java中的字符是Unicode编码,是双字节的。
本章知识点关系图:
2.1 File类
- File:File类是代表磁盘的文件或文件夹(目录)。
- 创建文件对象:
(1)File(String pathname)。该构造方法通过将给定路径名字符串转换为抽象路径名来创建一个新的File实例。语法:File fi1 = new File("F:\\桌面文件\\Text.txt");
(2)File(String parent,String child)。根据parent路径名字符串和child路径名字符串创建一个新的File实例。File fi2 = new File("F:\\桌面文件\\","word.txt");
(3)File(File f,String child)。第一个参数是一个new创建的文件夹。File dir = new File("F:\\桌面文件\\");File fi3 = new File(dir,"worddir.txt");
。 - File类的使用:
(1)操作:创建文件,删除文件等。
(2)状态:文件是否存在,文件是否隐藏等
(3)属性:文件名,文件绝对路径,文件大小,文件的修改时间等。
(4)如果文件以及存在,则会创建失败,即:不能覆盖。
(5)常用方法:
方法 | 返回值 | 功能 |
---|---|---|
f.getName() | String | 获取文件名称 |
f.canRead() | boolean | 判断文件是否为可读的 |
f.canWrite() | boolean | 判断文件是否可被写入 |
f.exits() | boolean | 判断文件是否存在 |
f.length() | long | 获取文件长度(字节) |
f.getAbsolutePath() | boolean | 获取文件的绝对路径 |
f.lastModified() | long | 获取文件最后修改时间 |
f.isHidden() | boolean | 判断文件是否为隐藏文件 |
package java_2_12;
import java.io.*;
import java.sql.Date;
import java.text.SimpleDateFormat;
public class Java_2_12 {
public static void main(String[] args)
{
/*
* 项目下的路径(默认路径):Text.txt
* 包中的文件:src/java_2_12/1.txt
* 也可以使用“\”:这样的话就要写两个\,比如src\\java_2_12\\1.txt
*/
//第一种创建文件的方法,一个参数。路径和文件名在一块
// File fi1 = new File("word.txt");//项目中的文件,创建文件对象
// File fi2 =new File("src/java_2_12/1.txt");//包中的文件--默认路径
File fi1 = new File("F:\\桌面文件\\Text.txt");//使用绝对路径创建文件
//第二种创建文件的方法,两个参数。路径和文件名分开。
File fi2 = new File("F:\\桌面文件\\","word.txt");//第一个参数是所在文件夹。第二个是文件名
//第三种创建文件的方法,两个参数。文件夹和文件名分开。
File dir = new File("F:\\桌面文件\\");//文件夹
File fi3 = new File(dir,"worddir.txt");//第一个参数是所在文件夹。第二个是文件名
System.out.println("文件是否存在: "+fi1.exists());//如果不存在,即没有创建。下面输出系统默认值。
System.out.println("文件名: "+fi1.getName());//输出文件名
System.out.println("绝对路径: "+fi1.getAbsolutePath());//输出文件绝对路径
System.out.println("是否是隐藏文件: "+fi1.isHidden());//是否是隐藏文件,返回布尔值
System.out.println("文件大小(字节数): "+fi1.length());//输出文件大小(字节)
Date date = new Date(fi1.lastModified());//通过毫秒值创建Date类
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");//时间的格式化类
System.out.println("文件最后修改时间: "+sdf.format(date));
if(fi1.exists())//如果文件存在,
{
boolean b = fi1.delete();//删除文件。返布尔值
System.out.println("文件是否删除成功: "+b);
}
try //创建文件,必须使用try-catch语句
{
//如果文件已经存在,则不能创建,没有覆盖的功能。
boolean creat = fi1.createNewFile();//
System.out.println("创建文件是否成功: "+creat);
}
catch (IOException e)
{
e.printStackTrace();
}
// System.out.println(fi1.equals(fi2));//比较两个文件是不是同一个文件。
}
}
- File类文件夹的操作:
(1)概括:创建文件夹、删除文件夹、文件夹是否存在、是否为文件夹、(*)获取所有子文件夹及子文件。
(2)为文件提供分层结构。
package java_2_12;
import java.io.*;
public class Demo {
public static void main(String[] args)
{
File dir = new File("dir");//在项目中创建文件夹,之后点刷新,就能看到文件夹。
boolean b = dir.mkdir();//创建文件夹,不能覆盖已有文件夹。如果存在,就创建不成功
// File dir = new File("dir/dir2/dir3");//多层文件夹
// boolean b = dir.mkdirs();//创建多层文件夹
// boolean del = dir.delete();//删除文件夹,如果是多层文件夹,就删除文件路径最后一个文件夹
System.out.println("创建文件夹是否成功: "+b);
File f = new File("F:\\桌面文件\\");//创建文件对象
File files[] = f.listFiles();//返回文件夹下所有的子文件和子文件夹,返回文件数组
for(File tmp:files)
{
if(tmp.isFile())//判断是否为文件
{
System.out.println("这是文件。"+tmp.getName());
}
else if (tmp.isDirectory())//判断是否是文件夹
{
System.out.println("这是文件夹: "+tmp.getName());
}
}
}
}
2.2 文件输入/输出流
(1)文件字节流:读取字节
- FileInputStream(文件字节输入流):用来读文件
- FileOutputStream(文件字节输出流):用来写文件
- 记得及时关闭流。
out.close();
,in.close();
package java_2_12;
import java.io.*;
public class Demo1 {
public static void main(String[] args)
{
File f = new File("word.txt");//创建文件对象
FileOutputStream out = null;//创建FileOutStream对象。
try //写文件的异常捕捉
{
//文件输出流,如果为true,在文件末尾添加内容,如果为默认的false,则会替换文件中内容
out = new FileOutputStream(f,true);
String str = "你见过洛杉矶凌晨四点的样子吗?";
byte b[] = str.getBytes();//创建字节数组,将字符串转换成字节数组
out.write(b);//将字节数组中的数据写入文件中
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
catch (IOException e) //out.write()的异常处理
{
e.printStackTrace();
}
finally
{
if(out!=null)//如果文件不为空,就关闭流
try
{
out.close();//关闭流
} catch (IOException e)
{
e.printStackTrace();
}
}
FileInputStream in=null;
try //读文件
{
in = new FileInputStream(f);
//创建缓冲区,一个汉字两个字节
byte b2[] = new byte[1024];//缓冲区
int len = in.read(b2);//将读取到的内容写到缓冲区中,len为读入到缓冲区的总字节数
//将字节数组自动转为字符串,从数组0索引取到len索引
System.out.println("文件中的数据是:"+new String (b2,0,len));
}
catch (FileNotFoundException e)
{
// TODO 自动生成的 catch 块
e.printStackTrace();
}
catch (IOException e)//in,read();的异常处理
{
// TODO 自动生成的 catch 块
e.printStackTrace();
}
finally
{
if(in!=null)
try
{
in.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
(2)文件字符流:读取字符
- FileReader(文件字符输入流):读文件,只要不关闭流,每次调用read()方法就顺序的读取文件内容。
- FileWriter(文件字符输出流):写文件
package java_2_12;
import java.io.*;
public class Demo2 {
public static void main(String[] args) {
// TODO 自动生成的方法存根
File f = new File("word.txt");//创建项目下的文件,创建文件对象
FileWriter fw = null;//创建字符输出流,写文件
try //写文件的异常捕捉
{
//默认为false,为true时,在原文件后添加内容
fw = new FileWriter(f,true);
String str = "自强不息";
fw.write(str);//将字符串写入到文件中
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
if(fw!=null)
{
try {
fw.close();
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
FileReader fr = null;
try {//读文件的异常捕捉
fr = new FileReader(f);
//如果读出的字符没有1024长,后面用空格补充
char ch[] = new char[1024];//创建缓冲区
int len;//已读出的字符数
while((len=fr.read(ch))!=-1)//如果文件读完,返回-1.
{//循环读取文件数据
System.out.println("文件中的内容为:"+new String(ch));
}
} catch (FileNotFoundException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}finally {
if(fr!=null)//流不为空
try {
fr.close();
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
}
2.3 带缓冲的输入/输出流
- 缓冲区:类似运送货物的货车,将数据一堆一堆的运送。
- 注意:先创建的流后关闭。
(1)缓冲字节流:包装其他字节流,提高效率 - BufferedInputStrean(缓冲输入字节流):
in = new FileInputStream(f);bi = new BufferedInputStream(in);
。 - BufferedOutputStrean(缓冲输出字节流):flush()方法将缓冲区的数据强制输出完。
主要代码
File f = new File("F:\\java资源\\jdk api 1.8.CHM");//创建一个File对象
BufferedInputStream bi = null;//创建字节缓冲流,很大程度上提高读写文件效率
FileInputStream in = null;//创建字节输入流
long start = System.currentTimeMillis();//获得流开始时的毫秒值
try {
in = new FileInputStream(f);
bi = new BufferedInputStream(in);//将文件字节流包装成缓冲流
byte b[] = new byte[1024];//创建缓冲区字节数组(这个缓冲区与Buffered不同)
while(bi.read()!=-1)//用缓冲流来读文件
{
}
long end = System.currentTimeMillis();//获得流结束时的毫秒值
System.out.println("经历的时间: "+(end-start));
}
File f = new File("Word.txt");
FileOutputStream out = null;//创建文件输出字节流
BufferedOutputStream bo = null;
try
{
out = new FileOutputStream(f);
bo = new BufferedOutputStream(out);//包装文件输出流
String str = "天生我材必有用,千金散尽还复来";
byte b[] = str.getBytes();//将字符串变成字节数组
bo.write(b);//用文件缓冲流来写,将数据写人缓冲区
//使用缓冲字节输出流时多使用刷新
bo.flush();//刷新,强制将缓冲区数据写入文件中,及时缓冲区没有满
}
(2)缓冲字符流:可以一以行为单位进行输入输出。
- BufferedReader(缓冲字符输入流):public String readLine()方法,读取一行文本。
- BufferedWriter(缓冲字符输出流):public void newLine()方法,创建新的一行,不需要换行
主要代码
File f = new File("Text.txt");
FileWriter fw = null;
BufferedWriter bw = null;
try {
fw = new FileWriter(f);
bw = new BufferedWriter(fw);//将文件字符输出流包装成字符缓冲流
String str = "世界这么大";
String str2 = "我想去看看";
bw.write(str);
bw.newLine();//创建新行
bw.write(str2);
File f = new File("Text.txt");
FileReader fr = null;
BufferedReader br = null;
try {
fr = new FileReader(f);
br = new BufferedReader(fr);//将文件字符输入流包装成缓冲字符输入流
String tmp=null;
while((tmp = br.readLine())!=null)//读一行数据
{
System.out.println(tmp);
}
2.4 数据输入/输出流
- 写数字时要避免写到一块,之间最好间隔一些其他的数据。
- DateInputStream(数据输入流)
- DateOutputStream(数据输出流)
主要代码
File f = new File("my.txt");
FileOutputStream out = null;//字节输出流
DataOutputStream dos = null;//数据流
try {
out = new FileOutputStream(f);
dos = new DataOutputStream(out);//将文件流包装成数据流
dos.writeUTF("这是写入字符串数据: ");//写入字符串数据
dos.writeInt(123);//写入整型数据
dos.writeDouble(3.1419526);//写入浮点型数据
dos.writeBoolean(true);//写入布尔值
}
DataInputStream di = null;//数据输入流
FileInputStream in = null;
try {
in = new FileInputStream(f);
di = new DataInputStream(in);//将文件流包装成数据流
System.out.println("readUTF: "+di.readUTF());
System.out.println("readInt: "+di.readInt());
System.out.println("readDouble: "+di.readDouble());
System.out.println("readBoolean: "+di.readBoolean());
}
2.5 ZIP压缩输入/输出流
- ZipEntry:条目,不支持中文
- ZipInputStream:解压缩时使用
- ZipOutputStream:压缩时使用
(1)压缩文件
方法 | 说明 |
---|---|
putNextEntry() | 返回值为void,开始写一个新的ZipEntry,并将流内位置移至此entry所指数据的开头 |
write(btye[] b,int off,int eln) | 返回值为void,将字节数组写入当前ZIP条目数据 |
finish() | 返回值为void,完成写入ZIP输出流的内容,无序关闭它所配合的OutStream |
SetComment(String comment) | 返回值为void, 可设置此ZIP文件的注释文字 |
package java_2_13;
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class Demo5 {
public static void compress()
{
File source = new File("F:\\桌面文件\\");//压缩的原文件
// File source = new File("C:\\Users\\lu\\Desktop\\png\\1.png");//压缩一个文件
File target = new File("C:\\Users\\lu\\Desktop\\lu1.zip");//压缩包
try(FileOutputStream fis = new FileOutputStream(target);
ZipOutputStream zos = new ZipOutputStream(fis))
{
if(source.isDirectory())//如果原文件是文件夹
{
//source.listFiles()方法获得文件夹下所有文件
for(File f:source.listFiles())//遍历文件夹下的所有文件
{
//对文件夹下所有文件添加条目
//这个hello就是压缩后给原文件添加上的名字,如果不想改变原名称,这里就写空格
addEntry(zos,"",f);//调用下面的方法压缩
}
}
else
{
addEntry(zos,"",source);//调用下面的方法压缩
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 卢
* @param zos:压缩流
* @param base:文件在压缩包中的路径
* @param source:被压缩的文件
*/
//创建条目的方法
static void addEntry(ZipOutputStream zos,String base,File source)//添加条目
{
if(source.isDirectory())//如果文件中有文件夹
{
for(File file:source.listFiles())
{ //递归
//因为base是压缩包中路径,所以它有一层父路径
//File.separator:系统默认分符.win:\;
addEntry(zos,base+source.getName()+File.separator,file);
}
}
else//如果只是文件,用流将文件写到压缩包中
{
byte buf[] = new byte[1024];//创建缓冲区,1024=1kb
try(FileInputStream fis = new FileInputStream(source))//为每一个文件创建条目
{
int con =-1;//读出的字节数
//在压缩包中添加一个新条目
zos.putNextEntry(new ZipEntry(base+source.getName()));//创建一个新条目
while((con =fis.read(buf))!=-1)//读出的字节数
{
zos.write(buf,0,con);//将缓冲区中的数据写入到压缩包中
zos.flush();//刷新
}
zos.closeEntry();//关闭条目
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
public static void main(String[] args)
{
compress();
}
}
(2)解压缩
static void decomp()//解压缩
{
File dir = new File("C:\\Users\\lu\\Desktop\\lu1\\");//解压后存放文件的文件夹
File source = new File("C:\\Users\\lu\\Desktop\\lu1.zip");//需要解压的文件
byte buf[] = new byte[1024];
ZipEntry entry =null;
try(FileInputStream fis = new FileInputStream(source);
ZipInputStream zis = new ZipInputStream(fis))
{
while(true)
{
entry = zis.getNextEntry();//通过压缩流一个一个的获取条目
if(entry==null)//如果条目为空,跳出循环
break;
if(entry.isDirectory())//如果条目为文件夹,跳过此次循环
continue;
File f = new File(dir,entry.getName());//dir为父目录,后面为文件名
if(!f.getParentFile().exists())//如果父文件路径不存在,就创建路径
{
f.getParentFile().mkdirs();
}
int count = -1;//读出了多少个字节
FileOutputStream fos = new FileOutputStream(f);//文件字节输出流,
while((count = zis.read(buf))!=-1)
{
fos.write(buf,0,count);
fos.flush();
}
fos.close();
zis.closeEntry();
}
} catch (Exception e) {
e.printStackTrace();
}
}
2.6 应用
(1)实现文件读取进度条
package java_2_14;
import java.io.*;
import javax.swing.*;
public class Demo1 {
public static void main(String[] args) {
byte b[] = new byte[2];//字节数组
try{
FileInputStream fis = new FileInputStream("word.txt");//实例化文件字节输入流
/*
* public ProgressMonitorInputStream(Component parentComponent,
* Object message,
* InputStream in)
* * message - 描述性文本放在对话框中,如果弹出。
* parentComponent - 触发被监视操作的组件。没有就写null
* in - 要监视的输入流。
*/
ProgressMonitorInputStream in =
new ProgressMonitorInputStream(null,"读取文件",fis);
while(in.read(b)!=-1)//读取文件,放入字节数组b中,如果读取完毕则返回-1
{
String s = new String(b);//将字节数组中的数据转换为字符串
System.out.print(s);
Thread.sleep(10);
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
三. 反射
问题:如何知道一个类有多少个方法?
答案:反射
- 反射:reflection
(1)程序可以访问、检测和修改它本身状态或行为的能力,即自描述和自控制。
(2)可以在运行时加载、探知和使用编译期间完全未知的类
(3)给Java插上动态语言特性的翅膀,弥补强类型语言的不足
(4)java.lang.reflect包中。 - 反射的功能
(1)在运行中分析类的能力,了解这个类有什么属性和方法。
(2)在运行中查看和操作对象,
(3)实现通用的数组操作代码
(4)类似函数指针的功能
3.1 访问构造方法
Constructor cons[] = c.getDeclaredConstructors();
获得所有构造方法,返回Constructor数组
constructor.getParameterTypes();
获得构造方法的参数类,constructor.getName();
获得构造方法的名字constructor.getModifiers()
获得构造方法的修饰符constructor.newInstance()
通过该构造方法利用指定参数创建一个该类的对象。
package java_2_14;
public class Demo3
{
int id;
String name;
Double pr;
public Demo3() //无参构造方法
{
super();
}
public Demo3(int id)//一个参数的构造方法
{
super();
this.id = id;
}
public Demo3(int id, String name) //两个参数的构造方法
{
super();
this.id = id;
this.name = name;
}
@Override
public String toString() //输出三个属性的值
{
return "Demo3 [id=" + id + ", name=" + name + ", pr=" + pr + "]";
}
private Demo3(int id, String name, Double pr) //私有的三个参数的构造方法
{
super();
this.id = id;
this.name = name;
this.pr = pr;
}
}
package java_2_14;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
public class Demo4
{
public static void main(String[] args)
{
try
{
Demo3 x = new Demo3();//实例化myDemo对象
Class c = x.getClass();//得到Class对象
Constructor cons[] = c.getDeclaredConstructors();//获得所有构造方法
for(Constructor con : cons) //遍历所有构造方法
{
con.setAccessible(true);
//输出构造方法的修饰符
System.out.print(Modifier.toString(con.getModifiers())+" ");
Class[] cs = con.getParameterTypes();//获得构造方法的参数类型,返回Class数组
System.out.print(con.getName() +" (");
for(int j=0;j<cs.length;j++)
{
//cs[j].getSimpleName()获得名字简称
System.out.print(cs[j].getSimpleName());
if(j<cs.length-1)
System.out.print(" ,");
}
System.out.print(" ){ }\n");
}
Constructor cs1 = c.getDeclaredConstructor();//获取无参数构造方法
Object obj1 = cs1.newInstance();//反射一个对象
System.out.println("无参:"+obj1.toString());
Constructor cs2 = c.getDeclaredConstructor(int.class);//获取有参数构造方法
Object obj2 = cs2.newInstance(123);//反射一个对象,给id赋值123
System.out.println("一个参数:"+obj2.toString());
Constructor cs3 = c.getDeclaredConstructor(int.class,String.class);//获取有参数构造方法
Object obj3 = cs3.newInstance(456,"jack");//反射一个对象,给id赋值123
System.out.println("两个参数:"+obj3.toString());
//三个擦参数的构造方法是private的
//无法直接反射对象,需要调用cs4.setAccessible(true);
Constructor cs4 = c.getDeclaredConstructor(int.class,String.class,Double.class);//获取有参数构造方法
cs4.setAccessible(true);//获取private的操作权限,设置为允许访问
Object obj4 = cs4.newInstance(456,"jack",3.14);//反射一个对象,给id赋值123
System.out.println("两个参数:"+obj4.toString());
}
catch (Exception e)
{
// TODO 自动生成的 catch 块
System.out.println("异常");
e.printStackTrace();
}
}
}
3.2 访问成员变量
利用反射获取类的成员变量,即:成员属性
Field成员变量类。
class.getFields();
获取公有成员变量
class.getDeclaredFidlds();
获取所有成员变量
field.getModifiers();
获得成员变量的修饰符field.getName();
获得成员变量的名字field.Type();
获得成员变量的声明类型
public class Demo3
{
public int id = 123;
public String name = "xxx";
private Double pr = 3.14;
}
package java_2_14;
import java.lang.reflect.*;
public class Demo5 {
public static void main(String[] args)
{
try
{
Demo3 x = new Demo3();//实例化myDemo对象
Class c = x.getClass();//得到Class对象
Field fs[] = c.getDeclaredFields();//返回所有公有属性
for(Field f1 : fs)//遍历所有属性
{
System.out.print(Modifier.toString(f1.getModifiers())+" ");//成员变量的修饰符
System.out.print(f1.getType().getSimpleName()+" ");//成员变量的类型
System.out.print(f1.getName()+" ");//成员变量的名字
System.out.println();
}
Constructor cs = c.getConstructor();//获得构造方法
Demo3 d = (Demo3)cs.newInstance();//获取反射对象
Field f = c.getDeclaredField("name");//获取反射对象的属性
System.out.println("name: "+f.get(d));
Constructor cs1 = c.getConstructor();//获得构造方法
Demo3 d1 = (Demo3)cs1.newInstance();//获取反射对象
Field f1 = c.getDeclaredField("pr");//获取反射对象的属性
f1.setAccessible(true);//设置为可访问
f1.set(d1, 1.2334555);//修改属性的值
System.out.println("pr: "+f1.get(d1));
}catch(Exception e)
{
e.printStackTrace();
}
}
}
3.3 访问成员方法
利用反射获取类的成员方法
java.lang.reflect.Mathod
class.getMethods();获取公有成员ff
class.getDeclaredMethods();获取所有成员方法
method.getModifiers();
获取成员方法的修饰符method.getName();
获取成员方法的名字method.getType();
获取成员方法的声明类型method.getParameterTypes();
获取成员方法的参数数组
package java_2_14;
public class Demo3
{
public void add(int a,int b)
{
System.out.println("a+b="+(a+b));
}
public void print()
{
System.out.println("这是print方法");
}
}
package java_2_14;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class Demo6 {
public static void main(String[] args)
{
Demo3 x = new Demo3();//实例化myDemo对象
// String x = new String();
Class c = x.getClass();//得到Class对象
Method me[] = c.getDeclaredMethods();//获取所有方法,返回Methods数组
for(Method m:me)
{
System.out.print(Modifier.toString(m.getModifiers())+" ");//获取修饰符
System.out.print(m.getReturnType().getSimpleName()+ " ");//回去返回值类型
System.out.print(m.getName()+"(");//获得方法的名字
Class gp[] = m.getParameterTypes();//获取方法的参数类型,返回值为Class数组
for(int i=0;i<gp.length;i++)
{
System.out.print(gp[i].getSimpleName());//参数的类型的名字
if(i<gp.length-1)//如果不是最后一个参数
{
System.out.print(",");
}
}
System.out.print(") { }");
Class gp2[] = m.getExceptionTypes();//返回所有异常
for(int i=0;i<gp2.length;i++)
{
System.out.print("异常:"+gp2[i].getSimpleName());
if(i<gp2.length-1)//如果不是最后一个参数
{
System.out.print(",");
}
}
System.out.println();
}
try
{
Constructor cs = c.getConstructor();//获取它的构造方法
Demo3 obj = (Demo3) cs.newInstance();//反射Demo3对象
Method m = c.getDeclaredMethod("print");//获取方法的对象
m.invoke(obj);//调用反射出的方法
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.4 使用Annotation功能
自定义注解
语法:public @interface MyAnnotation{ String value(); Class type(); }
。@interface声明的关键字
使用:@MyAnnotation
- 元注解:为其他注解做注解
(1)@Decumented:将注解文档化
(2)@Inherited:指示注解类型被自动继承
(3)@Retention:指示注解的注释要保留多久
(4)@Target:指示注解类型所适用的程序元素的种类 - 反射注解:我们写的注释,在程序运行时,在控制台我们并不能看到。但是通过反射注解,可以看到。
- 在定义Annotation类型时,可以通过Annotation类型@Target来设置Annotation类型适用的程序元素种类,如果未设置,表示适用所有程序元素。枚举类ElementType中的枚举常量用来设置@Target。
枚举常量 | 说明 |
---|---|
FIELD | 表示用于成员变量和枚举常量 |
METHOD | 表示用于方法 |
- 通过Annotation类型@Retention来设置Annotation的有效范围。枚举类RetentionPolicy中的枚举常量用来设置@Retention。如果未设置,则它的有效范围为枚举常量CLASS表示的范围
枚举常量 | 说明 |
---|---|
SOURCE | 表示不编译Annotation到类文件中,有效范围最小 |
CLASS | 表示编译Annotation到类文件中,但运行时不加载Annotation到JVM中 |
RUNTIME | 表示在运行时加载Annotation到JVM中,我们通过操作,然后可以在控制台看见,有效范围最大 |
package java_2_14;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)//注解可以用在哪里,这里是只能给成员属性做注解
@Retention(RetentionPolicy.RUNTIME)//运行时将注解加载到JVM中,此注解可以被反射
public @interface CellAnnotation
{
String remarks() default "";//备注
boolean enable() default true;//属性是否启用
}
package java_2_14;
public class Cell
{
@CellAnnotation(remarks="手机名")
public String name;//手机名
@Deprecated//过时注解
@CellAnnotation(remarks = "价格",enable = false)//属性不启用
public Double pri;//价格
@CellAnnotation(remarks = "厂商")
public String produced;//厂商
}
package java_2_14;
import java.lang.reflect.Field;
public class Demo7
{
public static void main(String[] args)
{
Class c = Cell.class;//创建Class对象,
Field fs[] = c.getDeclaredFields();//获取所有成员变量
for(Field f:fs)
{
//获取每一个属性的注解
if(f.isAnnotationPresent(CellAnnotation.class))//是否被注解过
{
//创建注解的对象
CellAnnotation p = f.getAnnotation(CellAnnotation.class);
System.out.print(f.getName()+"属性的注解内容:");
System.out.print("备注="+p.remarks());//返回注解的内容
System.out.println(",属性是否有效="+p.enable());//返回属性是否启用
}
}
}
}