java SE总结:
1. 文档注释可以在:类,常量,方法上声明
文档注释可以被javadoc命令所解析并根据内容生成手册
2. 字符串是不变对象:字符串对象一旦创建,内容是不可改变的,要想改变内容一定会创建新对象。
字符串若使用字面量形式创建对象,会重用以前创建过的内容相同的字符串对象。
使用new关键字创建的字符串对象由于不会存入常量池也不会检查常量池,所以不会重用对象。
java编译器有一个优化措施,就是若计算表达式运算符两边都是字面量,那么编译器在生成class文件时就将结果计算完毕并保存到编译后的class文件中了。
3. String使用了final修饰,不能被继承
方法:
1)int length():该方法用来获取当前字符串的字符数量,无论中文还是英文每个字符都是1个长度
2)int indexOf(String str):查看给定字符串在当前字符串中的位置。
首先该方法会使用给定的字符串与当前字符串进行全匹配,当找到位置后,会将给 定字符串中第一个字符在当前字符串中的位置返回。常用来查找关键字使用。
重载方法:
int lastIndexOf(String str):返回给定的字符串在当前字符串中最后一次出现的位置
3)String substring(int start,int end):截取当前字符串的部分内容
从start出开始,截取到end(但是不含有end对应的字符)
Java API有个特点,凡是使用两个数字表示范围时,通常都是“含头不含尾”的。
重载方法:
sub=str.substring(4):只需传入一个参数,从给定的位置开始连续截取到字符串末尾
4)String trim():去除当前字符串中两边的空白
5)char charAt(int index):返回当前字符串中给定位置处对应的字符
6)boolean startsWith(String str):用来判断当前字符串是否是以给定的字符串起始的
boolean endsWith(String str):用来判断当前字符串是否是以给定的字符串结尾的。
7)String toUpperCase()
String toLowerCase():将一个字符串中的英文部分转换为全大写或全小写(验证码使用)
8)valueOf():String 提供了一组静态方法
该方法有若干的重载,用来将其他类型数据转换为字符串。
常用的是将基本类型转换为字符串。
4. StringBuilder:
内部维护了一个可变的字符数组,从而保证了无论修改多少次字符串内容,都是在这个数组中完成。当然,若数组内容
被超出,会扩容,但是与字符串的修改比较,内存上的消耗是明显的。
其提供了用于修改字符串内容的常用修改方法:
增:append 删:delete 改:replace 插:insert
StringBuilder和StringBuffer:
——StringBuilder:线程不安全,同步处理,性能慢。(JDK1.5)
——StringBuffer:线程安全,并发处理,性能快。(JDK1.0)
5.正则表达式:
[abc]:a,b,c中任意字符
[^abc]:除了a,b,c中的任意一个字符
\d:任意一个字符,相当与[0-9]
\w:单词字符,相当于[a-zA-Z0-9_]
\s:空白字符,相当于[\t\n\x0B\f\r]
X?:表示0个或1个X
X*:表示0个或任意多个X
X+:表示1个到任意多个X
方法:
1)boolean matches(String regex):该方法用来使用给定的正则表达式验证当前字符串的 格式是否满足要求,满足则返回true。
需要注意的是,无论给定的正则表达式中是否有^...$都是做全匹配验证
2)String[] split(String regex):将当前字符串中满足正则表达式的部分“切掉”,保留剩 下的内容。
3)String replaceAll(String regex,String replacement):该方法会将当前字符串中满足正则 表达式的部分替换为给定的字符串。
6.重写Object的toString方法:
何时需要重写:
通常只要会在程序中使用当前类实例的toString方法就应当重写。原因在于Object 实现的toString格式为:类型@地址,不能具体说明当前类的特征信息,所以我们需要 重写。重写后返回的字符串没有具体的格式要求,原则是将需要关注的属性信息拼在一 起以字符串的形式返回。
重写Object定义的equals方法,该方法的作用是判断当前对象与给定的对象“像不像”。实际就是根据自身的需求来定义判断标准。
对于引用类型而言,保存的是对象的地址。
“=="比较的是变量的值,那么这里就相当于比较的是两个地址是否一样,除非这两个变量指向同一个对象,否则地址
肯定不同,由此可以理解为“==”比较的为:“是不是同一个”
System.out.println(Object o):
该方法可以将任何java中的对象输出到控制台。
实际上,它输出的是给定的对象toString()方法返回的字符串。
Java API中大部分的类都重写了toString,若是我们自己定义的类的实例需要被该方法输出到控制台,通常这个类就需要重写toString
7.包装类:
为了解决基本类型数据不能参与面向对象开发这个问题
其中6个表示数字类型的包装类都继承自Number,Number是一个抽象类,提供了可以将数字在不同类型间相互转换的方法,但是通常都是短类型向长类型转换
例如:short--->int
int--->short也可以,但是由于short长度小,转出来的数字可能不正确。
Number提供的转换基本类型的方法
1)int intValue():转换为int值
2)double doubleValue():转换为double值......
基本类型转换为包装类推荐的方式:
使用静态方法valueOf()
数字类型包装类都支持两个常量:
MAX_VALUE:对应的基本类型的最大取值
MIN_VALUE:对应的基本类型的最小取值
包装类都支持一个静态方法parseXXX(String str):
可以将字符串转换为对应的基本类型
前提是:该字符串描述的内容必须是基本类型数据的样子。
自动装箱、自动拆箱:
Interger i=Integer.valueOf(1)----自动装箱
int ii=i.intValue()----自动拆箱
8.java.util.Date:
其每一个实例都可以表示一个确切的时间点。内部维护了一个long值,这是一个相对值,相对的是UTC(1970年元旦)到这个时间点之间相差的毫秒数。
我们现在常用的计量时间的标准是格林威治时间。
Date now=new Date();
默认创建出来的Date实例表示当前系统时间。
方法:
1)long getTime():该方法可以获取当前Date对象维护的long值。即:1970年元旦,到当前Date所表示的时间之间经过的毫秒数。
9.java.test.SimpleDateFormat:
该类的主要作用是根据一个给定的日期格式,将String与Date之间相互转换。
学习该类需要记住两件事:
1:会写日期格式
2:记住两个转换方法
<Date-->String>
String format(Date date):
该方法会将给定的Date表示的时间按照当前SimpleDateFormat指定的日期格式转换为字符串
<String-->Date>
Date parse(String str)
根据SimpleDateFormat制定的日期格式解析给定的字符串,将其转换为一个Date 对象来表示该字符串描述的时间。
10.java.util.Calendar:
日历类,用于操作时间的类。
本身是一个抽象类,不能实例化,常用的实现类是封装了格里高利历法的Calendar
Calendar提供了一个静态方法getInstacne(),以方便我们获取一个使用的实现类的实例。 Calendar calendar=Calendar.getInstance();
<Calendar-->Date>
Date getTime():
该方法可以将当前Calendar表示的时间以Date形式返回
<Date-->Calendar>
void setTime(Date date)
该方法允许当前Calendar表示给定的Date所表示的时间
对于时间相关内容的学习,需要掌握:
11:Date,SimpleDateFormat,Calendar各自的作用
12:String,Date,Calendar之间的转换
Calendar提供了个set方法,允许我们设置当前Calendar表示的时间
方法:
1)void set(int field,int value):将给定的时间分量设置为给定的值
2)int get(int field):该方法可以获取当前Calendar中给定的时间分量对应的值。
3)void add(int field,int value):Calendar提供了一个可以计算时间的方法,是当前Calendar加上给定的时间分量对应的值。计算后可能会影响其他时间分量的值,会自动对应好。
例如,加了几天后,星期就会跟着变。若跨月了,月也会跟着变等等。
4)Calendar.DAY_OF_YEAR
Calendar.DAY_OF_MONTH:Calendar提供了一个可以获取当前Calendar表示的时间 中给定的时间分量所允许的最大值。
***月份从0开始,0表示1月,1表示2月。。。也可以使用Calendar提供的常量
***和“日”相关的时间分量:
DAY_OF_MONTH:月中的天,俗称几号
DAY_OF_WEEK:星期中的天,周几
DAY_OF_YEAR:年中的天
DATE:这个等价于DAY_OF_MONTH
***HOUR:12小时制
HOUR_OF_DAY:24小时制
13.java.util.Collection:
是一个接口,所有集合的顶级接口,规定了所有集合都应具有的功能(方法),集合用来存放一组元素。并提供了维护集合的相关文法。
Collection c=new ArrayList();
方法:
1)boolean add(E e):集合提供了一个用于向集合中添加元素的方法,若成功添加,则返回 true
2)int size():该方法用来获取集合中的元素数量
3)boolean isEmpty():判断集合是否不含有任何元素
4)void clear():清空集合
5)boolean contains(E e):判断一个集合是否包含给定的元素,若当前集合包含给定的元素则返回true
****集合存放的是元素的引用(地址)
14.泛型:
泛型的实际类型是Object。若不指定泛型的具体类型,默认就是Object。
泛型并非是真正的将Point中的属性以及方法参数和返回值改成了Integer,而是创建
的泛型真实类型为Object,只不过将其当作Integer去看待。
eg:Point<Integer> p=new Point<Integer>(1,1);
Point<String> s=new Point<String>("你","好");
----ClassCastException:类型转换异常
Point<Integer> p=new Point<Integer> (1,2);
Point p2=p;
p2.setX("你好");
****由于x已经是String类型,所以当以p的角度获取时,自动强转为Integer时
会抛出ClassCastException,类造型异常
15.Collection:
集合提供了批量操作元素的方法。
方法:
1)boolean addAll(Collection c):批量添加的方法,将给定的集合中的所有元素添加到当
前集合中,当做完此操作后若当前集合元素发生了变化,则返回true。
2)boolean containsAll(Collection c):判断当前集合是否包含给定集合中的所有元素,若
都包含则返回true
3)boolean remove(E e):将集合中第一个与给定元素equals比较为true的元素删除。
若成功删除元素返回true,否则返回false
4)Iterator iterator():遍历集合元素,该方法可以获取一个用来遍历当前集合的迭代器。
迭代器:迭代器Iterator本身是一个接口,值定义了便利集合的方法应该有哪些,
但并没有具体实现。不同的集合都提供了用于遍历自身元素的迭代器实现类。
遍历集合遵循的原则:问取删,其中删除元素不是必须操作。
Iterator it=c.iterator(); //获取用于遍历当前集合的迭代器
迭代器方法:
1)boolean hasNext():问的过程,该方法会判断当前集合中是否还有元素可以被
取出,若有则返回true
2)E next():取的过程:该方法会将集合中下一个元素返回。<E指泛型>
注意:
1)if("#".equals(str)){...}
当我们需要判断一个变量与字面量内容是否一样时,应当使用字面量的
equals方法去与变量比较,折可以避免空指针异常(NullPointerException)。
2)c.remove(str);(错)
Iterator it=c.iterator();
it.remove();(正确)
***使用迭代器遍历集合的过程中不能通过集合的方法改变元素数量,否则
可能导致迭代器遍历失败。我们可以通过迭代器提供的remove方法删除通过 next方法遍历出来的元素。
泛型:集合和迭代器也都是支持泛型的!!!
16.集合---->数组:toArray():
toArray()方法要求我们传入一个要转换的数组,长度与集合的size一致即可。若不一致,toArray()会判断当前数组是否可以保存的下集合中的所有元素,可以就是用这个数组,
不可以,则创建一个与给定的数组同类型的数组并将集合元素存入后返回。
数组---->集合:asList():Arrays.asList()
注意:1)只能转换为List集合。转换需要是用到数组的工具类Arrays
2)数组转换回来的集合,不能添加新元素.原因在于对该集合的操作就是对数组的操作。
3)若想对集合元素进行增删这样的操作,需要自己创建一个新集合。
4)方法:list.set(0,"1");替换了集合元素就是替换了数组对应的元素
17.增强for循环:
新循环,也叫做:增前for循环,增强循环,for_each
注意:
新循环在遍历集合时要注意。新循环本身不是新的语法,JVM并不认可。而是编 译器认可的。它会将新循环改成迭代器后再生成class文件。所以,使用新循环遍历集 合的过程中同样
不能使用集合的方法改变元素数量。(其实就是迭代器)
18.java.util.List
是一个接口,并集成了Collection接口。
特点:元素可重复,并且有序。允许以下标的形式操作元素。
方法:
1) E get(int i):获取制定下标对应的元素
2) E set(int index,E e):将给定的元素设置到指定的位置上,返回值为原位置上的元素
3) void add(int index,E e):将给定的元素插入到给定的位置,原位置及后续元素顺序向后移动。
4) E remove(int index):将给定下标对应的元素删除,并将其返回
5) List subList(int start,int end):获取当前集合中指定范围内的子集(取子集)
注意:
如果将子集中每个元素扩大十倍,对子集的操作会影响原集合中对应的元素,子集 元素清除后,原集合中这部分元素也被删除了,快速删除部分内容可以用到。
----就是子集元素改变,原集合元素也将被改变。
19.排序集合:
排序通过集合的工具类Collections的静态方法sort实现自然排序
(***)Collection和Collections的比较:
----Collections:操作集合的工具类
----Collection:是一个接口
1.1)对集合进行自然排序:
按首字母排列,大写在前,小写灾后,同首字母,比较第二个字母,以此类推
eg:排序后:[Allen, Bill, boss, jack, jason, marry, rose]
2)由于字符串排序的方式是按照字符的编码从小到大的顺序,排列中文会出问题。
排序中文没有规律。
3)排序自定义类型元素:
若想使用Collections的静态方法sort(List list)进行自然排序,那么该元素必须实现 Comprable接口,并定义比较规则才可以。
原理:
1)Comparable接口:---->必须重写compareTo方法
当我们实现了Comparable接口后,该接口要求我们必须重写compareTo方法。该 方法的作用是使当前对象与给定对象比较大小。返回值是一个int值,该值不关心具体 取值,只关心取值范围。
若返回值<0:当前对象比给定的对象小
若返回值=0:两个对象相等
若返回值>0:当前对象必给定的对象大
判断标准,点到原点的距离长的大
2)Comparator接口:---->必须重写compare方法
为什么要使用Comparator接口进行比较大小:
该重载的sort方法要求我们额外的传入一个比较器,这样做就会解决两个问题:
20.元素自身已经实现Comparable接口并定义了比较规则,但是该规则不能满足我 们排序的需要。
21.元素没有实现Comparable,这里也不需要强制元素必须实现该接口,减少对该元 素的侵入。
***侵入性:为了使用它提供的功能,需要对我们定义的类有所改变。
eg://自定义比较器
MyComparator comm=new MyComparator();
Comparator <Point> comm=new Comparator<Point>(){
public int compare(Point o1,Point o2){//重写compare方法
int len1=o1.getX()*o1.getX()+o1.getY()*o1.getY();
int len2=o2.getX()*o2.getX()+o2.getY()*o2.getY();
return len1-len2;
}
};
class MyComparator implements Comparator<String>{//实现Comparator接口
public int compare(String o1,String o2){
return o1.length()-o2.length();
}
}
//使用Collections的sort排序方法:
Collections.sort(list,comm);---->传入两个参数。comm比较器,并按照比较器的 比较规则对集合进行自然排序。
22.队列:<支持泛型>
队列是一个接口,不能实例化
与集合相似,可以保存一组元素。但是不能任意访问其中元素。存取必须遵循(先进先出)原则。
由于LinkedList可以存放一组元素,并且增删效率比较高,所以其也实现了Queue接口,可以看作是一个队列使用。
eg:Queue<String> queue=new LinkedList<String>();
方法:
1)boolean offer(E e):向队列末尾追加一个新元素
2)E poll():从队首取元素,并且将该元素从队列中删除
3)E peek():该方法也会获取队首元素,但是不会做出队操作,该元素不会从队列中被删除
遍历队列:
for(int i=queue.size();i>0;i--){//<===>while(queue.size()>0)---->推荐使用while循环
str=queue.poll();//从队首取元素
System.out.println(str);
}
23.栈:
存放一组元素,存取必须遵循先进后出原则一般应用于操作的可追溯性(后退功能)
java没有为栈单独设计类型,使用双端队列实现,只调用一侧的进出队方法,就行 成了栈。不过双端队列由于具有栈的特性,所以为此给栈单独定义了从
一侧进出的两个方法:push,pop
方法:
1)void push(E e):将元素“压入”栈中,入栈操作,新进去的元素在栈顶(第一个位置)
2)E pop():出栈操作。获取栈顶元素。获取后该元素会从栈中删除。
3)peek同样可以使用,同队列中peek()方法。
遍历栈:(与队列相似)
while(stack.size()>0){
str=stack.pop();
System.out.println(str);//返回栈中被删除的元素
}
如何使用队列与栈:
1)后退----栈
2)排队----队列
24.java.util.Map--------->Map不允许放重复元素
Map中每个元素由两部分组成key:value
所以Map看起来像是一个多行两列的表格,Map主要解决的问题是遇到保存value, 但是value自身可读性不高,需要加个“标签”加以
解释含义。在Map中key是不允许重复的,是否重复取决与key的equals比较。
方法:
1) V put(K k,V v):
将给定的key与value存入当前Map中。由于key在Map中不允许重复,
所以使用相同的key存入不同元素,就是替换value操作,被替换的value
会被返回。若存放的key在map中不存在,返回值则为null
注意:
当调用put方法后需要获取返回值时,若value的类型是包装类,切忌接收时用其对应的基本类型,这会导致自动拆箱。若返回值为null,则自动拆箱时会抛出空指针异常!
2)V get(K k):
根据给定的key获取对应的value。若给定的key在Map中不存在,则返回null
3)V remove(K k):
从Map中删除给定的key所对应的这一条记录,(key,value都会删除),并将这个 key对应的value返回。
Map也提供了可以查看给定的key或value是否在Map中被包含的方法,
------->常用的是查看key是否被包含。
方法:
1)boolean containsKey(K k):
判断当前Map中是否包含给定的key,判断依据依然是根据key的equals比较的。
2)boolean containsValue(V v):
判断当前Map中是否包含给定的value
遍历Map
有三种方式:
1:遍历所有的key
2:遍历所有的value(不常用)
3:遍历每一组键值对
25:遍历所有的key
Set<K> keySet()
该方法会将当前Map中所有的key存入一个Set集合中,然后将集合返回。
那么遍历该集合就相当与遍历了所有的key
26:遍历所有的value(不常用)
Collection<V> values()
该方法会将当前Map中所有的value存入一个集合后返回。所以遍历当前集合
就相当于遍历了所有的value.
27:遍历每一组键值对
Set<Entry<K,V>> entrySet()
在Map中每一组键值对是由一个Entry的实例保存的,所以该方法会将每一个Entry实例都存入一个Set集合,然后将该集合返回。遍历该集合拿到每一个Entry就相当于渠道了每一组键值对,达到遍历的目的
28.HashMap、hashCode:
HashMap中作为Key的元素的HashCode方法的重写与equals的重写直接影响着HashMap检索性能。所以要妥善的重写。
JDK文档中规定,当我们重写一个类的equals方法时,就应当连同重写HashCode方法。
并且,两个方法有一个对应关系:
1)若两个对象equals方法比较为true,那么HashCode值一定要相等。
2)反过来,若两个对象HashCode值相同,最好也保证两个对象equals比较为true,否则会影响散列表性能。
HashCode()方法自身的重写规则:
一个对象的HashCode值在该对象equals比较中参与的属性没有被改变的前提下,多次调用返回的值应当相同。
1.java.io.File:
该类用来表示一个文件或目录,使用它可以:
1:访问文件或目录的属性(名字,大小,最后修改时间等)
2:操作文件或目录(创建,删除)
eg:
File file = new File("."+File.separator+"test.txt");
1)表示当前项目根目录下的文件test.txt,在描述文件或目录时,我们指定的路径最好,使 用相对路径,否则会出现系统差异,不利于跨平台。
2)".":当前目录,在eclipse中运行时,表示当前项目的根目录
3)java.io.File
由于目录的层级分隔符不同系统也有差异:
windows:"\",例如:d:\xxx.txt
linux:"/"
所以应当使用File的常量表示:File.separator
获取属性信息的相关方法:
1)获取文件名:String getName()
2)获取文件大小:long length()
3)查看文件是否真实存在:boolean exists()
4)查看是否表示的是文件:boolean isFile()
5)查看是否表示的是目录:boolean isDirectory()
6)以下三个方法判断文件或目录是否:可写,可读,可运行
file.canWrite();
file.canRead();
file.canExecute();
是否为隐藏文件:file.isHidden();
2.使用File创建一个新文件
---->在当前项目根目录下创建一个名为demo.txt的文件
若是在当前目录下,"./"是可以忽略不写的
File file = new File("demo.txt");
//若该文件不存在则创建
if(!file.exists()){
//创建新文件
file.createNewFile();
System.out.println("创建完毕!");
}
方法:
1)使用File删除一个文件:file.delete();
2)使用File创建一个目录:file.mkdir();
3)创建多级目录:file.mkdirs();
--mkdirs()在创建当前目录的同时,将所有不存在的父目录一同创建
4)删除目录:file.delete();
若是删除目录,前提是该目录必须是一个空目录(目录中没有任何内容),否则删 除不掉
3.File操作:
1)若File表示的是一个目录,我们可以获取该目录下的所有子项,子项无非还是文件或目录。
/*
* 获取当前项目根目录下的所有子项
*/
File dir = new File(".");
if(dir.isDirectory()){//判断文件表示的是否是目录
/*
*方法:
* File[] listFiles()
* 该方法会获取当前目录下的所有子项,每一个子项无非还是文件或目录,所 以,可以用若干File对象表示每一个子项,最终将他们存入一个数组返回。
*/
File[] subs = dir.listFiles();
for(File sub : subs){
if(sub.isFile()){
System.out.println("文件:"+sub.getName());
}else{
System.out.println("目录:"+sub.getName());
}
}
}
2)FileFilter:文件过滤器
使用文件过滤器来获取一个目录下满足条件的部分子项。是一个接口,本身不能实例化,我们需要定义一个类实现该接口,并重写其定义的accept方法来定义过滤规则。
accept方法用于定义过滤要求,当给定的file对象满足要求就返回true即可。
重载的listFiles方法:
要求我们传入一个文件过滤器,然后该方法会将当前目录下的所有子项顺序的传递给过滤器的accept方法,只将返回为true的子项保留,最终存入一个数组并返回。eg:File[] subs = dir.listFiles(filter);---->filter为过滤器
3)删除多级目录:
由于一个目录中可能含有子项。所以要删除当前这个目录必须先将其所有子项删除才
可以。
4.java.io.RandomAccessFile
----->用于读写文件数据的类
创建时有两种模式:
只读模式:对文件只进行读取操作---->r
读写模式:可以对文件进行编辑------>rw
方法:
1)向文件demo.dat中写入数据
RandomAccessFile raf= new RandomAccessFile("demo.dat","rw");
2)void write(int d):
该方法会向文件当前指针指向位置写出1个字节,该字节写的是给定的int值的"低八位"
3)raf.close():
使用RAF读写完文件后,一定要记得close,释放底层资源
4)int read():
从文件中当前指针位置处读取一个字节,并以int形式返回。该int值只有"低八位"有效。若返回的int值为-1,表示读取到文件末尾。
案例:复制文件:
思路:创建两个RandomAccessFile,一个用来从原文件读取,另一个用来写到目标 文件中。
RandomAccessFile src= new RandomAccessFile("fos1.txt","r");
RandomAccessFile desc= new RandomAccessFile("fos2.txt","rw");
int d=-1;//保存每次读取的一个字节
//复制效率低,原因在于一次读写的数据少,导致硬盘频繁读写。向提高读写效 率就要提高读写的数据量,减少读写的次数从而提高效率。
while((d = src.read())!=-1){
desc.write(d);
}
desc.close();
src.close();
1.RandomAccessFile提供了一个可以一次性写出很多字节的方法。
String提供了可以用来将当前内容转换为对应的一组字节的方法:
1) byte[] getByte()
将当前字符串按照当前系统默认字符集转换为对应的一组字节,这里不同的操作 系统可能存在差异。windows是GBK,linux是UTF-8
2) byte[] getBytes(String charset)
将当前字符串按照给定的字符集转换为一组字节
常用的字符集:
GBK:国标编码
UTF-8:unicode的自己,俗称万国码。互联网常用字符集编码
常见的:
ISO8859-1:欧洲编码, 不支持中文
2.RandomAccessFile提供了可以一次性读取若干字节的方法。
int read(byte[] d)
该方法会试图一次性读取给定的字节数组总长度的字节量,并将这些字节顺序的存
入到给定的字节数组中。返回值为实际读取到的字节量,若返回值为-1,则表示读取到文
件末尾了。
3.RandomAccessFile读写基本类型数据
RandomAccessFile raf=new RandomAccessFile("test.dat","rw");
long pos=raf.getFilePointer();//获取RAF指针位置,刚创建出来的RAF指针默认执向文件的第一个字节。下标为0
void seek(long pos):若想读取文件最开始的int值,需要先将指针移动到相应的位置上
连续读取4个字节并转换为int值返回,若读取到文件末尾,会抛出EOFException的异常。EOF(end of file)
eg:int i=raf.readInt();
连续读取8个字节,并将long值返回,同样的,若读取到文件末尾,会抛出异常。
eg:long l=raf.readLong();
4.java.io.FileInputStream
文件字节输入流,也是低级流。作用是读取文件
java.io.FileOutputStream
文件字节输出流,负责将数据写出到文件中的流
FileOutputStream若使用一个参数的构造方法创建,则为覆盖写操作。
意思是,每次创建流对给定文件写数据前,都会先将该文件中的所有数据清除后再进行新的写操作。
eg:FileOutputStream fos=new FileOutputStream("fos.txt");------>覆盖写操作
FileOutputStream支持重载的构造方法,允许传入第二个参数,该参数是一个boolean值,当该值为true时,FileOutputStream就是追加写操作。会在当前文件末尾开始写入新数据。
eg:FileOutputStream fos=new FileOutputStream("fos.txt",true);----->追加写操作
5.java.io.ObjectInputStream
该高级流的作用是可以方便的读取一组字节,然后将其还原为原本的java对象。前提是这一组字节必须是由ObjectOutputStream将一个对象转换的一组字节!
6.java.io.ObjectOutputStream
通过该流可以方便的将对象转换为一组字节,然后该流就会通过其处理的fos将这一组字节写入文件保存中了。
void writeObject(Object o):
ObjectOutputStream提供的该方法用于将给定的对象转换为一组对应的字节然后通过其处理的流写出。
若想被writeObject方法转换为一组字节,前提是该对象所属的类必须实现了可序列化接口,否则会抛出异常。
序列化:将现有的数据结构转换为一组字节的过程。。。起始也就是编码的过程,将数据写入到文件中(磁盘中)的过程,称之为“持久化"
7.BufferedOutputstream(缓冲字节输出流)
通过缓冲流写出的数据不是立刻写出,而是先将数据存入到其内部的一个字节数组中,只有字节数组满了,才会真的写出一次。
方法:
bos.flush():
该方法比较强制,不管缓冲区是否被装满,否要将缓冲区中现有的数据一次性写出去,要清楚,频繁调用flush会提高写出次数,
但是可以做到写出具有即时性。
bos.close():
close方法内部会在关闭前自动调用flush一次。
8.BufferedInputStream(缓冲字节输入流)
使用缓冲流,可以提高读写效率
read()方法:
并不是只读取了一个字节。BIS内部维护了一个缓冲区(字节数组),当我们调用
read方法读取一个字节时,实际上BIS会从它处理的流中一次性
读取若干字节,并存入内部的字节数组中,然后将第一个字节返回。这样的做法好处在于,当我们再次调用read方法读取一个字节时,它会直接将字节
数组中下一个字节返回,而不是再去读取。所以原理还是一次读取若干字节,减少读取次数提高的读取效率。
eg:while((d=bis.read())!=-1){
bos.write(d);
}
9.序列化与反序列化:
序列化:对象是存在于内存中的。有时候我们需要将对象保存到硬盘上,又有时我们需要将对象传输到另一台计算机上等等这样的操作。这时我们需要将对象转换为一个字节序列,而这个过程就称为对象序列化。
反序列化:相反,我们有这样一个字节序列需要将其转换为对应的对象,这个过程就称为对象的反序列化。
1)若当前类的实例希望可以被ObjectOutputStream进行序列化,那么该类必须实现Serializable接口。
2)当我们实现了Serializable接口后,通常我们都要定义下面这个常量来维护版本号。
版本号对于对象的反序列化有着很重要的作用。
在反序列化时:若原来的对象版本号与现有的版本号不同,反序列化直接失败,原 因:版本号不一致。
若原来的对象版本号与现有的版本号相同,则启用兼容模式:
原来对象有的属性,现在还有的则还原
原来对象有的属性,现在没有了就忽略
原来对象没有的属性,现在有的则使用默认值。
3)transient关键字
用来修饰一个属性,被修饰的属性在序列化的时候值被忽略,这样做可以达到
让对象瘦身的目的。除此之外该关键字没有其他作用。
1.java.io.InputStreamReader
字符输入流
该高级流的目的:方便我们按照给定的字符集读取字符。它会将对应的字节自动转换为字符。
2.java.io.OutputStreamWriter
字符输出流
以字符为单位写出数据,字符流都是高级流,方便我们读写字符,底下本质还是要读写字节,字符流只是帮我们在字节与字符之间做了转换。
常用的两个构造方法:
1)OutputStreamWriter(OutputStream out)
该构造方法会将给定的字节流进行包装,然后当前流就会按照系统默认的字符集将要写出的字符串转换为对应的一组字节,在通过给定的字节流写出。
2)OutputStreamWriter(OutputStream out,String charsetName)
使用给定的字符集将字符串转换为一组字节后写出。通常我们使用当前流的目的就是要将字符串按照给定的字符集写出,所以这个构造方法最常用。
3.java.io.PrintWriter
缓冲字符输出流
可以以行为单位写出字符串,缓冲功能起始是靠BufferedWriter实现的,PW除此之外还有一个好功能:自动行刷新。
对文件直接操作的构造方法:
1)PrintWriter(File file)//文件名
2)PrintWriter(String path)//路径
1.1)PrintWriter包装其他的流
eg: FileOutputStream fos=new FileOutputStream("pw1.txt");
PrintWriter pw=new PrintWriter(fos);
可以直接将字节输出流转换为缓冲字符输出流.这样做,
优点:在于简单。
缺点:在于不能按照指定字符集写出字符串,只能按照系统默认字符集写出。
1.2)按照指定的字符集创建PrintWriter
eg:FileOutputStream fos=new FileOutputStream("pw2.txt");
OutputStreamWriter osw=new OutputStreamWriter(fos,"UTF-8");
PrintWriter pw=new PrintWriter(osw);//按照制定字符集创建
1.3)凡是使用流的形式创建PrintWriter,那么构造方法都支持第二个参数。该参数为boolean型,当值为true时就具有了自动行刷新。
***自动行刷新:每当我们调用println()方法写出一行字符串后就会自动flush()写出。
这样做会降低写出效率,但是写出具有即时性。
注意://调用println的时候会自动flush
pw.println(line);
4.java.io.BufferedReader
缓冲字符输入流
特点:以行为单位读取字符串
方法:
String readLine():
BufferedReader提供了上述方法,该方法会尝试读取若干字符,直到读取到换行符
为止,然后将换行符前的所有字符组成一个字符串然后返回。这个字符串
中不含有换行符!
若返回值为NULL,表示再没有数据可读。若一行中只有一个换行符,那么返回值
为空字符串,也不会是NULL!
5.java异常捕获机制
当JVM(虚拟机)运行时遇到了异常后,会创建一个该类异常的实例,并封装完整的出错报告信息,然后将该异常抛出。
异常处理机制中的finally块,finally块可以直接跟在try{}后面,类似:
try{
...
}finally{
}
更常见的是跟在最后一个catch之后。
finally是异常捕获机制的最后一个部分,其后不能再添加catch。
finally块中的代码是无条件执行的,无论try语句中的代码是否出错。通常将释放资源等操作放在finally中。
当调用了一个具有throws声明抛出异常的方法时,我们就要解决该异常,解决方法有两种:
1:使用try-catch捕获并处理。
2:在当前方法上继续声明throws将异常抛出,但不要在main方法上声明throws
***Java异常:
分为:可检测异常,非检测异常
——可检测异常:可检测异常经编译器验证,对于声明抛出异常的任何方法,编译
器将强制执行处理或声明规则,不捕捉这个异常,编译器就同步过,不允许编译。
——非检测异常:非检测异常不遵循处理或者声明规则。在产生此类异常时,不宜
定非要采取任何适当操作,编译器不会检查是否已经解决了这样一个异常。
Runtime Exception类属于非检测异常。
Exception常用API
---PrintStackTrace 输出执行堆栈信息
---getMessage
---getCause
自定义异常:
通常是描述业务级别的错误,这里描述的是年龄不合法的异常
步骤:1)自定义异常类 extends Exception
2)定义好自定义异常后,通过Eclipse来自动生成相应的构造方法。
(Source——>Generate Constructors from Superclass)
2.public static void main(String[] args) {
System.out.println(test("0")+","+test(null)+","+test(""));//3,3,3
}
public static int test(String str){
try{
return '0'-str.charAt(0);
}catch(NullPointerException e){
return 1;
}catch(Exception e){
return 2;
}finally{
return 3;
}
}//切记:注意finally中return 3;
1.线程:
用来解决“同时”做多个任务。没有绝对意义上的同时干,线程都是走走停停的并发运行。但是要表现的作用是“各干各的”
线程有两种创建方式:
第一种:继承Thread类并重写run方法
第二种:实现Runnable接口,并重写run方法。
注意:
1)启动线程不要调用run方法,而是应当调用start()方法。只有这样做该线程才会被纳入线程调度,具有并发运行的能力。
2)start方法会很快执行完毕,并不是start方法执行的过程中run方法被执行了。而是start方法执行后,当该线程第一次获取时间片时开始运行run方法。
3)线程不能控制什么时候获取CPU的时间片,也不能决定时间片的长短。这些都是由线程调度管理。它会尽可能的均匀的将时间片分配给每一个线程。但也不保证一个线程一次这样交替分配。
第一种创建线程的方式存在两个明显的不足:
1:由于java是单继承,这就导致了我们要是继承了Thread就不能再继承其他父类。
2:由于线程要执行的任务被定义在线程的run方法当中,这就导致了一个问题:线程
与线程要执行的任务有一个强耦合关系,这不利于线程的重用。
最好的状态是:线程只负责并发运行,给什么任务就运行什么任务。
耦合:就是所谓的依赖关
优点:创建简单,使用匿名内部类创建时建议使用该方式。
第二种创建线程的方式:
1:实际上是单独定义线程要执行的任务。这里实现了一个与线程无关的接口Runnable,由于java接口可以多实现,所以并不影响再实现其他接口,同时由于没有继承其他类,那么也可以自行指定父类。
2:由于我们单独指定了线程要执行的任务,并且在创建线程的时候才将任务指派进去,而不是在线程内部定义任务,这就解决了线程与任务之间的耦合关系。
**方法:
static Thread currentThread():
Thread提供了一个静态方法,该方法用于获取运行这个方法的线程
进程中至少要有一个前台线程,而当java虚拟机启动后就会启动一个前台线程来运
行main方法
进程结束:当一个进程中的所有前台线程都结束了,进程就结束了,由于我们只有
运行main方法的这一个前台线程,所以当main方法执行完毕,该线程完成任务结束。
进程中就没有前台线程了,所以进程结束了。
2.获取线程状态信息的相关方法:
long getId():返回该线程的标识符
String getName():返回该线程的名称
int getPriority():返回线程的优先级
Thread.state getState():获取线程的状态
boolean isAlive():测试线程是否处于活动状态
boolean isDaemon():测试线程是否为守护线程
boolean isInterrupted():测试线程是否已经中断
3.线程优先级:
线程的优先级有10个等级,1最低,10最高,5默认,也可以使用Thread提供的常量:
MIN_PRIORITY:最低优先级,对应数字:1
MAX_PRIORITY:最高优先级,对应数字:10
NORM_PRIORITY:默认优先级,对应数字:5
原则上,优先级高的线程被分配CPU时间片的次数多
4.守护线程:
又叫做后台线程。使用上与前台线程没有区别,唯一在结束上有区别。当进程结束时,无论后台线程是否还在运行都要强制结束。
jack.setDaemon(true);//设置为守护线程
GC就是运行在一个守护线程上的。
5.1)Thread 提供了一个静态方法sleep
用于让运行sleep的线程进入阻塞状态指定毫秒。当超时后,该线程会自动回到Runnable状态等待分配CPU时间片再次运行。
2)Thread 提供了一个方法join
该方法允许当前线程进入阻塞状态等待另一个线程工作完毕。通常需要协调两个线程工作的时候就要使用该方法。
3)Object上定义了两个方法wait,notify
当一个线程调用了一个对象的wait方法后,该线程就进入阻塞状态,直到这个对象
的notify方法被调用,该线程才会解除阻塞。
使用这两个方法完成线程同步操作的即时性更强,join必须等到线程工作完毕才可以,
即时性相对较差。
API文档上有说明:当我们调用某个对象的wait或者notify方法时,该方法需要使
用synchronized块进行同步,而锁定对象就是
wait与notify所属的对象。否则在实际运行过程中会发生异常。
6.线程池:
通常池的概念是为了解决两个问题:重用,控制数量。
所以线程池(ExecutorService)主要也是为了解决:
重用线程
控制线程数量
何时用:
当我们的程序需要频繁创建线程和销毁线程时,就应当考虑使用线程池来维护了。
eg:
1)ExecutorService threadPool=Executors.newFixedThreadPool(2);//创建一个固定大小的线程池,线程数量为2条。
2)threadPool.execute(runnable);//调用execute方法,将任务runnable指派进去
3)threadPool.shutdown();--->结束线程池
shutdownNow()--->立马结束线程池
1.将集合与Map转换为线程安全的
ArrayList,LinkedList,HashSet,HashMap都不是线程安全的。多线程情况下操作他们都会产生线程安全问题。
我们可以使用Collections这个集合的工具类将上述的数据结构转换为线程安全的。
HashSet---无序不可重复
ArrayList---有序可重复
eg:
1)List<String> list=new ArrayList<String>();
list.add("one");......
//该静态方法用于将给定的List集合转换为一个线程安全的List
list=Collections.synchronizedList(list);//转过来为线程安全的
2)Map<String,Integer > map=new HashMap<String,Integer> ();
map.put("语文", 98);.....
//将给定的Map转换为线程安全的
map=Collections.synchronizedMap(map);//转过来为线程安全的
注意:
线程安全的集合和Map都将存取元素的方法修饰了synchronized,所以多个线程调
用存方法是同步的,取也一样,并且由于这些方法都修饰了,所以他们之间还存在互斥
效果。意思就是说当两个线程一个调用存元素的同时,另一个线程也不能取。
但是,当我们使用迭代器遍历集合或Map的时候,遍历的过程与集合存取之间没
有互斥性!我们知道遍历集合的过程中不能通过集合的方法修改元素数量,但是由于遍
历与增删之间没有互斥,就可能导致其他线程在这个过程中增删了元素,导致遍历出现
异常。
2.当多个线程同时操作一个数据时,由于线程切换的不确定性,可能会导致出现逻辑混乱,严重时可能导致系统崩溃。
3.synchronized关键字可以修饰方法
当修饰了方法,该方法就是“同步”方法,多个线程不能同时访问方法内部。必须排队做。这就解决了多个线程同时操作统一数据导致的混乱问题。
synchronized若修饰的是方法,锁对象则是当前方法所属的对象,在这个例子中就是main方法中定义的table对象。
4.在方法上使用synchronized虽然可以起到同步的效果,但是由于整个方法所有代码变成同步,对于并发执行的效率来讲会有所降低。除非整个方法所有代码都需要这样做,否则我们应当使用synchronized块的形式,将需要同步的代码括起来减小同步范围,这样在保证安全的前提下也提高了并发执行的效率。
5.synchronized体现的互斥性
当一个对象的两个方法都被synchronized修饰,则两个方法就有了互斥性。
当synchronized块包含了两段不同的代码,而这两格块锁对象相同,则这两块代码也有互斥性。
//同一段代码,同一个对象--同步
//不同的方法,同一个对象--互斥
静态方法上被synchronized修饰后,该静态方法变为同步的。由于静态方法与对象无关,和类相关,所以无论调用哪个对象的这个静态方法都具有同步性。
当然,静态方法应当使用类名.方法()的形式调用,而不应当使用对象调用。静态方法锁的是类对象(描述当前类的对象),该对象的实际类型为Class,虚拟机在加载我们使用的每个类时都会创建一个Class实例来描述它,以便了解该类有哪些属性方法等。所以每个类只有一个类对象。
一.TCP:
聊天室服务端
1)ServerSocket运行在服务端的Socket(套接字)
服务端运行的Socket:(功能)
1)负责申请端口,以便客户端通过该端口链接到我们的服务器。
2)第二个功能就是接收客户端的连接,并生成与该客户端通讯的Socket。
2)ServerSocket提供的方法:
Socket accept():该方法是以个阻塞方法,会一直监听其打开的8088端口,等待一个客户端连接,一旦连接,该方法会返回与该客户端交互的Socket。
3)创建Socket时通常要传入两个参数
1:远程计算机IP地址
2:服务端应用程序申请的服务端口创建的过程就是连接的过程,只有与服务端建
立了连接才会创建成功,否则会抛出异常。
二.UDP
服务端:
收数据过程:
1:创建Socket
2:准备一个接收数据用的包
3:将包交给Socket,用于接收客户端发送过来的数据。
当接收后,包就有了很多变化:
1:包中就有客户端发送过来的数据了
2:包也知道实际发送过来的数据量有多少
3:包也知道了客户端的地址信息(便于我们通过该地址回复客户端)
4:取数据
客户端:
过程:
1:创建使用UDP通讯的socket
2:准备数据
3:打包
创建包的同时,填地址,放数据
4:通过socket发送
1.XML:
指可扩展标记语言(EXtensible Markup Language),是独立于软件和硬件的信息传输工具,应用于 web 开发的许多方面,常用于简化数据的存储和共享。
Eclipse中添加.jar包,并创建路径:
1)在项目下---->右击new---->Folder---->命名lib
2)在项目下---->右击Build Path---->Configure Build Path---->点击Libraries---->点Add JARS
2.XML指令
处理指令,简称PI (processing instruction)。处理指令用来指挥解析引擎如何解析XML文档内容。
<?xml version=“1.0” encoding=“utf-8” ?>
在XML中,所有的处理指令都以<?开始,?>结束。<?后面紧跟的是处理指令的名称。
XML对大小写是敏感的,这一点不象HTML。
XML要求必须有根元素,所谓根元素就是不被其它元素包围(不含有父元素)。并且根元素只能有一个。
3.XML中转义:
实体引用 | 字符 | 说明 |
< | < | 小于 |
> | > | 大于 |
& | & | 与字符(和字符) |
' | ' | 单引号 |
" | “ | 双引号 |
4.文本数据:
1)<!-- 文本 -->
2)CDATA段:
格式:< ! [ CDATA [ 文本内容 ] ] >
5.XML解析:
1)SAX解析方式
SAX(simple API for XML)是一种XML解析的替代方法。相比于DOM,SAX 是一种速度更快,更有效的方法。它逐行扫描文档,一边扫描一边解析。而且相比于 DOM,SAX可以在解析文档的任意时刻停止解析。
其优缺点分别为:
优点: 解析可以立即开始,速度快,没有内存压力
缺点: 不能对节点做修改
2)DOM解析方式
DOM(Document Object Model, 即文档对象模型) 是 W3C 组织推荐的处理 XML 的一种方式。DOM解析器在解析XML文档时,会把文档中的所有元素,按照其 出现的层次关系,解析成一个个Node对象(节点)。
其优缺点分别为:
优点:把xml文件在内存中构造树形结构,可以遍历和修改节点
缺点: 如果文件比较大,内存有压力,解析的时间会比较长
6.Document:
Document对象是一棵文档树的根,可为我们提供对文档数据的最初(或最顶层)的访问入口。
Element用于描述XML中的一个元素。元素可包含属性、其他元素或文本。如果元素含有文本,则在文本节点中表示该文本。
Document的方法:
Element getRootElement():用来获取当前XML文档中的根元素。
7.Element:
1)element方法
Element element(String name):该方法用于获取当前元素下指定名字的子元素。
2)elements方法
List elements():用于获取当前元素下所有子元素
重载方法:
List elements(String name):获取当前元素下同名的所有子元素
3)getName方法
String getName():用于获取当前元素的名字
4)getText方法
String getText():用于获取元素的文本节点(其实标记与结束标记之间的文本)
5)attribute方法
Attribute attribute(int index):获取当前元素的指定属性,index为索引,从0开始。
重载方法:
Attribute attribute(String name):获取当前元素的指定名字的属性。
8.Attribute:
Attribute类的每一个实例用于描述一个元素中的某个属性信息
1)getName方法和getValue方法
String getName() : 获取属性的名字
String getValue() : 获取属性的值
9.写XML
1)构建Document对象
我们可以使用Dom4j提供的一个类:DocumentHelper,其提供了一个静态方法:
static Document createDocument():该方法会创建一个Docuemnt对象。
2)Document的addElement方法
Element是用来描述XML中的一个元素的
Element addElement(String name):向文档中添加根元素,并返回该元素,该方法只调用一次,否则会抛出文档已经存在根元素的异常。
3)Element的addElement方法
Element addElement(String name):向当前元素中添加给定名字的子元素,并将其对应的Element对象返回。
4)Element的addAttribute方法
Element addAttribute(String name,String value):向当前元素中添加给定名字以及对应的值的属性,返回值依然为当前元素。
这样做的好处在于连续追加元素时代码书写简洁。
5)Element的addText方法
Element addText(String text):向元素的文本节点添加文本信息。
6)XMLWriter输出XML文档
10.XPATH:
1)路径表达式
2)谓语
3)通配符
附:
1)使用DOM解析XML文档的大致流程
1:创建SAXReader
2:读取XML文档数据并进行解析(读取就解析了),返回Document对象,Document对象就表示XML文档信息了
耗时操作,会将XML信息全部载入内存
3:获取Document对象获取根元素
4:根据XML文档结构,通过根元素逐层遍历,最终达到遍历XML文档的目的。
2)生成XML文档的大致步骤:
1:创建Document对象,表示文档
2:向Document中添加根元素
3:向根元素中添加若干层子元素完成文档结构
4:创建XMLWriter
5:将Document写出,若写入文件则会生成一个XML文件。若通过Socket获取的输出流写出则是将XML数据发送至远端计算机
6:关闭XMLWriter