框架基础:反射
-------------------------------------------------------------------------------------------
正则表达式只关注格式
X{n,m} n可以等于0,但不能留空。最多个数的m可以留空,留空则表示任意多个X
分组:"()"
(abc || def){3}
abcdefabc 三个数为一个整体;
[^abc]除了ab从的任意字符
[a-z&&{^bc]]a到z中除了b和c以外的任意一个字符,其中&&表示“与”关系
[a-z]a到z中的任意一个字符
[a-zA-Z0-9]a到z,A到Z和0到9中任意一个字符
String regex = "\\d"; //[0-9]
String regex = "\\D"; //[^0-9]
^\w{ 8,10 }$ 表示整体字符串只能出现单词字符8-10个。
(+86|0086) 表示这里可以是+86或者0086。
————————————————————————————————————————
"."点儿,在正则表达式中表示任意一个字符。
"\"在正则表达式中是转意字符,当我们需要描述一个已经被正则表达式使用的特殊字符时,我们就可以通过使用"\"将其转变为原本的意思。
"\"在正则表达式中也有一些预定义的特殊内容:
\d:表示任意一个数字
\w:表示任意一个单词字符(只能是 数字,字母,下划线)
\s:表示任意一个空白字符(\t \r \n \f \x0B)
\D:表示任意一个非数字字符
\W:表示任意一个非单词字符
\S:表示任意一个非空白字符
2、"字符集合 []"
"[]"用来描述单一字符,方括号内部可以定义这个字符的内容,也可以描述一个范围。例如:
[abc]:表示该字符只能是a或者b或者c
[123]:表示该字符只能是1或者2或者3
当我们需要描述所有小写字母时,我们可以使用范围 [a-z],表示该字符可以是任意一个小写字母。
同样还可以使用 [0-9] 来表示该字符可以是任意一个数字。
也可以在多个范围内选择。比如,[a-zA-Z0-9_] 表示该字符可以是任意字母,数字以及"下划线"。
3、"*"、"+"、"?"
通常我们需要描述的字符串会有很多重复出现的元素,但又不需要严格限制出现的次数时,我们就可以使用"*","+"这些量词。
例如:邮箱地址,那么在"@"字符前允许出现若干字符作为用户名。这时候我们就可以使用"\w+"来描述这里至少出现一个单词字符了。
"+":表示内容可以连续出现至少1次以上
"*":表示内容出现0-若干次
"?":表示内容出现0-1次
4、{n}、{n,}{n,m}
除了前面讲到的量词外,有时我们也需要要求内容出现的次数有具体要求。比如手机号码。这时我们要求出现的数字就不能是一个模糊的概念了,而必须要求11位。又比如我们要求用户输入密码时,要求密码是6-15位。遇到这类问题是,我们可以使用:
{n}:表示内容必须出现n次
{n,m}:表示内容出现n-m次
{n,}:表示内容出现至少n次
例如,\d{11} 就表示数字只能出现11位,这样就解决了上述的问题。
1.1.2. 分组
通过上面的内容,我们还无法解决类似下面的问题:
在描述电话号码时,前面有区号,区号的形式可以是0086或者+86
那么我们如何在这两个字符串之间选择?
这时我们可以使用分组"()"。() 可以将内容看做一个整体,()中可以使用"|"来表示或关系。例如,(+86|0086) 表示这里可以是+86或者0086。
1.1.3. "^"和"$"
通过在正则表达式的开始添加"^"以及末尾添加"$"来表示一个整体。若不使用它们,那么正则表达式只匹配某个字符串的部分内容是否符合格式规则,但使用它们,则要求字符串必须从头到尾都满足该格式规则。
例如,^\w{ 8,10 }$ 表示整体字符串只能出现单词字符8-10个。
——————————————————————————————————————————
1.2.1. matches方法
matches()方法的参数要求我们传入一个用字符串描述的正则表达式,然后使用该正则表达式描述的字符串格式规则来匹配当前字符串,若满足那么该方法返回true。否则返回false。
例如:
String emailRegEx =
"^[a-zA-Z0-9_.-]+@([a-zA-Z0-9-]+\\.)+[a-zA-Z0-9]{2,4}$";
String email = "bjliyi@tarena.com.cn";
System.out.println(email.matches(emailRegEx));//true
1.2.2. split方法
String[] split(String regex):参数要求传入一个用字符串描述的正则表达式,然后使用该正则表达式描述的字符串规则来匹配当前字符串,并按照满足的部分将字符串拆分。
例如:
String str = "java,c#,php,javascript";
String[] array = str.split(",");
//[java,c#,php,javascript]
System.out.println(Arrays.toString(array));
1.2.3. replaceAll方法
String replaceAll(String regex,String replacement):参数要求传入一个用字符串描述的正则表达式和一个需要替换的字符串,然后使用该正则表达式描述的字符串规则来匹配当前字符串,并将满足的部分替换为需要替换的这个字符串。
例如:
String str = "abc123bcd45ef6g7890";;
str = str.replaceAll("\\d+", "数字");
System.out.println(str);//abc数字bcd数字ef数字g数字
——————————————————————————————————————————————————————
^[abc]{3}$ java中的字符串中[abc]{3}不带^ $也表示全匹配,其他语言不行。
^必须要abc开头
$必须abc结尾
[]表示可选,没有表示必须
fancq@tedu.cn
jackson,25,男,5000,2008-12-22
[a-zA-z]{1,20},[0-100],["男""女"],[\d],[\d]-[1-12]-[1-31]
[a-zA-Z0-9_]@[a-zA-Z0-9_].([a-zA-Z]) 缺量词
.在表示任意字符,需要.则需要转义字符
[a-zA-Z0-9_]+@[a-zA-Z0-9_]+(\.[a-zA-Z]+) +
()表示分组
java中.没有特殊含义,不同与正则表达式。需要再补\
面向对象3:
封装 继承 多态
object类有toString和equals方法,继承了建议重写该方法
==比较的是地址指向的对象是否一致
equals比较的是对象
void dosome(Object obj){}
int i=1;
基本类型不继承于Object,不是对象。因此dosome(i)是报错的
可以定义一个类Integer,该类是继承Object的
基本数据类型不能直接参与面向对象开发,因此需要包装类
更推荐使用valueOf,Integer但只用一个字节保存,127到-127之间,超过比如128则重新开辟一个空间,也就是new
double的话valueOf都是直接new新对象;
String str1 = "123.123";
String str = "123";
//int i1=Integer.parseInt(str1); //编译报错,数值格式异常,int存不下123.123
int i=Integer.parseInt(str);
'''''''''''''''''''''
//jdk1.5开始类自动装箱。这里实际上已经变成:
int i=Integer.valueOf(123).intValue();编译器自己补充了
int i=Integer.valueOf(123);
//jdk1.5开始类自动装箱。这里实际上已经变成:
Integer in=Integer.valueOf(123);编译器自己补充了
Integer in =123;
软件:
box2d:模拟物理效果 c语言写的 ->jbox2d
JMonkey Engine
---------------------------------------------------------------------------------------
day03 Collection
/**
* java.util.Collection
* 集合
* 集合和数组一样,用于保存一组元素,但是其提供了用于操作和维护集合元素的相关方法,使用起来更便捷
* Collection接口定义了所有集合都具备的功能
* 其派生了两个子接口
* java.util.List 可重复集且有序
* java.util.Set不可重复集,大部分实现类是无序的
* 元素是否重复是依靠元素自身equals比较的结果
*
*/
跟之前学的的接口案例一样,不能new 接口,但是可以这样:
接口 引用=new 该接口的实现类
Collection c = new ArrayList();//是接口,不能new,但是可以new其实现类
/*
* boolean add(E e)
* 将给定元素添加到集合中
* 添加成功则返回true
*/
c.add(1); //数据类型也可以,但是其实是自动装箱了
/*
* boolean contains(E e)
* 该方法会根据给定元素与集合现有元素equals
* 比较的结果来判定结果是否包含该元素
*
* 如果Point的equals方法注释掉,则调用==方法判断,结果返回的是false————因为都new了对象,因此地址值不同
*/
Calendar calendar = Calendar.getInstance();
/*
* Calendar的实现类都重写类toString,只是直观看不出具体日期
*
*/
System.out.println(calendar);
/*
* Calendar ->Date
* 使用Calendar提供的方法:
* Date getTime();
* 该方法会将当前Calendar表示的日期以一个Date类型实例返回。
*/
Date date = calendar.getTime();
System.out.println(date);
/*
* Date->Calendar
* Calendar提供的方法:
* void setTime(Date date)
* 该方法可以使当前Calendar表示给定的Date所表示的日期
*/
calendar.setTime(date);
//设置一个分量值,其他尽量不改变
calendar.set(calendar.DAY_OF_WEEK,Calendar.MONDAY);
System.out.println(calendar.getTime());
——————————————————————————————————————————————————————————————
什么是迭代器呢?
其实就是集合的取出元素的方式
集合的size()跟数组的length不同,集合的是当前的元素个数,长度是不固定的,最大长度也就是int的长度
集合的isEmpty判断的只是是否空元素,而集合是中是存在的,并不是null的。null则是集合不存在
substring(intdexbegin,indexend) 可以中间截取
---------------------------------------------------------------------------------------------------------------
day04 Collection
集合中的常用方法:
add
removeAll
containsAll
集合不能像数组一样用下标获取元素,只能用Iterator迭代器
/*
* Iterator的三个方法:
* boolean hasNext()
* E next() 返回迭代的下一个元素。
* void remove() 从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。
*/
-----------------------------------------------------------------------------------
/*
* 在使用迭代器遍历集合的过程中
* 是不可以通过集合的方法增删元素的。
*/
//c.remove(str); //这是c也就是Collection的删除方法,错误的!!
/*
* 迭代器提供的remove方法可以将调用迭代器next方法取出的元素从集合中删除。
*/
it.remove(); //这是迭代器的删除方法,正确的
------------------------------------------------------------------------------------
增强型for循环,也叫新循环,增强循环,for each ,JDK1.5以后推出的一个新特性
仅仅用来遍历集合或数组元素使用
特性都是编译器认可,不是虚拟机认可。
增强for作为一个新特性也是编译器认可的,编译器自动变成了Iterator,因此
增强for循环的判断中删除元素还是不能用集合的方法,还是只能用Iterator的增删方法。然而不能再次调用迭代器,再用迭代器的增删方式,因为for其实已经用了迭代器的方法了。
所以
使用新循环遍历集合元素时不可以通过集合分方法增删元素,因为本质就是迭代器遍历
---------------------------------------------------------------------------------------
泛型:
第一位不能是数字,后面可以。如<T1>可以,<1T>不行,错误
调用泛型类的时候必须是数据类型的不能是基本类型,如
class Point<T>{
private T x;
private T y;
构造方法....
}
class TestPoint{
public static void main(String [] args){
Point <Integer> p1 = new Point <Integer> ();
Point <Double> p2 = new Point <Double> ();
Point <String> p3 = new Point <String> ();
}
}
还可以设置多个类型
class Point<X,Y>{
private X x; //X类型的x
private Y y; //Y类型的y
构造方法....
}
class TestPoint{
public static void main(String [] args){
Point <Integer,Double> p = new Point <Integer,Double> ();
Point <Double,Double> p2 = new Point<Double,Double> ();
Point <String,Integer,> p3 = new Point <String,Integer> ();
}
}
-------------------------------------------------------
泛型的实际原型都是Object,new出来在堆里面就是Object,跟迭代器一样其实质也算编译器实现的,而不是虚拟机认可的
------------------------------------------------------------------------------------
Point <Integer> p1 = new Point<Integer>(1,2);
//编译器检查实际的值是否为Integer
p1.setX(12);
//p1.setX("12"); //编译不通过,不是Integer值
/*
* 编译后的class文件中有强制类型转换
* int x1 =(Integer)p1.getX();
*/
int x1= p1.getX();
System.out.println("x1:"+x1);
/*
* 可以不指定泛型,不指定则使用原型Object
*/
Point p2 =p1;
p2.setX("一");
String x2 = (String)p2.getX();
System.out.println("x2:"+x2);
//堆内存中的x的类型已经被p2改为类String类型
x1 = p1.getX(); //类造型异常
System.out.println("x1:"+x1); //常见面试题:这里输出值是?答:无法走到这一步,上面已经异常了
-----------------------------------------------------------------------------------------
数组和集合类同是容器,有何不同:
数组虽然也可以存储对象,但长度是固定的。而集合长度是可变的
数组中可以存储基本数据类型,集合只能存储对象
集合类的特点:
集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象
为什么会出现这么多的容器呢?
因为每一个容器对数据的存储方式都有不同。
这个存储方式称之为:数据结构
集合作为一个容器,应具备哪些功能,如已学的StringBuffer容器
增删改查
___________________________________________________________________________________________
集合框架
--ArrayList 更适合查询
--LinkedList 更适合插入和删除,首尾更快
--Vector
-List
Collection--
-Set
--HashSet
--TreeSet
可以通过下标操作的:get set 重载的remove
调用clear()方法之后再输出
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
List的set和remove方法均返回被加入或删除的指定位置的元素
List <Integer>list = new ArrayList<Integer>();
subList:
对子集的操作就是对原来集合该部分元素的操作
集合转数组:---------------------------------> toArray:
给的数组能用就用,不能用就自己new一个
比如集合只有3个元素,
String[] array= c.toArray(new String[c.size()]);
c.size()是根据集合的长度给出数组长度,如果给多了就为剩下的就为空,如果少了toArray就自己new一个同类型的数组
当数组转换成集合后,对集合的操作就是对数组的操作,比如set后集合变了,数组也一样变了
从数组中转换来的集合是不能添加新元素的
数组转集合:--------------------------------->asList()
//数组转成集合。调用Arrays的第一个方法也就是asList()
List<String> list = Arrays.asList(array);
* 数组切换为集合
* 使用数组的工具类Arrays的静态方法asList //静态方法只跟参数有关,也就是只传参即可
* 需要注意,仅能转换为List集合
/*
* 向集合中添加元素“five”
* 数组转换的集合是不可以添加新元素的
* 因为这会导致数组的扩容,就不能表示原来的数组了。
* 所以添加元素会抛出不受支持的异常
*/
//创建一个新的的同时把旧的作为传进构造方法中去,但是不能是重复的,否则只会有一个元素。不同对象也可以传进去
/*
* 所有集合都支持一个可以用Collection作为参数的构造方法。
* 该构造方法的作用是在创建当前集合的同时包含给定集合中的所有元素
*/
List<String > list1 = new ArrayList<String>(list);
//将旧的list传进新建的list1中去,这样新的list1就有了旧的list的元素,而且list1也可以增加新的元素
//list1.addAll(list); //将旧集合的元素整体传进新集合,用addAll更多,省事,简单
list1.add("five");
System.out.println(list1);
}
——————————————————————————————————————————————————
day04 Homework test01
boolean re= c1.remove("two"); //remove(Object o);必须是被删除的元素,而不能是下标
-------------------------------------------------------------------------------------------------------
day05:
List排序:只是对集合的操作
数组的工具类:Arrays 此类包含用来操作数组(比如排序和搜索)的各种方法。此类还包含一个允许将数组作为列表来查看的静态工厂。 包括
静态的方法asList,
静态的sort方法,
静态的toString方法
静态的copyOf方法
静态的equals方法
集合的工具类:Collections 此类完全由在 collection 上进行操作或返回 collection 的静态方法组成。
常见面试题:
Collections和Collection的区别是?
colletion是集合的接口,collections是工具类
联想,对于线程池executorService的executor和exetutors也一样,
前者是接口,后者是工具类,有各种方法比如静态的newFixedThreadPool(int nThreads)
——————————————————————————————————————————————
/**
* 结合排序
* 使用集合的工具类Collections的静态方法sort
* 需要注意的是,仅能对List集合排序
* sort方法会对List集合元素进行自然排序(从小到大)
*
*/
哪个类实现了Comparable,泛型写自己就行了
但是用系统的Comparable比较方法具有侵入性
public class Point implements Comparable<Point>{
......
@Override
/**
* 该方法的作用是用于判断当前对象this与参数对象o比较大小
* 返回值不关注具体数值,只关注取值范围
* 当返回值>0 : 当前对象大于参数对象
* 当返回值<0 : 当前对象大于参数对象
* 当返回值=0 : 两个对象相等
*/
public int compareTo(Point o) {
//由于x和y是坐标,因此比较到原点的距离
int len = this.x*this.x+this.y*this.y;
int olen = o.x*o.x+o.y*o.y;
return len-olen;
//反过来排序,由大到小。认为谁大就返回谁的值
//return olen-len;
}
}
如对汉字进行排序,具体可看day05----> SortListDemo3类
list.add("苍老师");
list.add("范老师");
list.add("小泽老师");
String类是final的,因此不能重写。但是Collections的sort提供了重载的方法
sort的重载方法
public static <T> void sort(List<T> list,Comparator<? super T> c)
Comparator相当与第三方,给两个参数,我帮你进行比较
Comparable是自己和别人比
——————————————————————————————————————
队列Queue:FIFO
栈Stack:先进后出,相当于底部堵死的,比如弹夹。有后退功能的时候用,如Windows进入文件夹后后退前进功能。
相当于管道,只能首尾增删
Queue:
boolean offer(E e);将一个对象添加至队尾,如果添加成功则返回
E poll();从队首删除并返回一个元素
E peek();返回队首的元素(但并不删除)
Queue也继承了Collection方法,因此也是集合的一种,Collection的方法也能用
LinkedList继承了Deque,而Deque继承了Queue
不使用迭代器只遍历一遍????
Queue的遍历建议用while,当队列的size大于0则一直把数据poll出
——————————————————————————————————————————
栈
双端队列Deque(继承自Queue)
push入栈
pop出栈
是最常见的方法
LinkedList继承了Deque,而Deque继承了Queue
stack.push("1");
stack.push("2");
stack.push("3");
stack.push("4");
System.out.println(stack); //push是先进后出,输出是4321
————————————————————————————————————————————
查询表:
Map查找表,相当于多行两列
贴个标签
如需要查找某人成绩:
key value
语文 98
数学 99
英语 97
物理 96
化学 98
Map的key不可以重复的
/**
* java.util.Map
* 查找表,常用的实现类:HashMap(散列表)
*
* Map的每一个元素分为两部分:key , value
* 其中key在Map中不允许重复(equals判断)
*/
Map重写了toString,输出的数值在{}内,跟字符串一样
——————————————————————————————————————————————————
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("语文",99);
map.put("数学",98);
map.put("英语",97);
map.put("物理",96);
map.put("化学",99);
System.out.println(map);
map的修改Entry只需要继续调用put方法即可,因为map的key不能有重复,有则覆盖,因此修改数据也就是还是用put方法
————————————————————————————————————————————————
Map遍历有3种方式:
1:遍历所有的key,
2:遍历每一组键值对(Entry)
3:遍历所有的value(相对不常用)
————————————————————————————————————————————————
1 * 遍历所有的key
keySet()
返回此映射中包含的键的 Set 视图。该 set 受映射支持,所以对映射的更改可在此 set 中反映出来,反之亦然。如果对该 set 进行迭代的同时修改了映射(通过迭代器自己的 remove 操作除外),则迭代结果是不确定的。set 支持元素移除,通过 Iterator.remove、Set.remove、removeAll、retainAll 和 clear 操作可从映射中移除相应的映射关系。它不支持 add 或 addAll 操作。
+++++++++++++++++++++++++++++++++++++++++++++++++——————————=++++++++++++++++++++++++++++++++++++++++++++++++
/*
* 遍历所有的key
* Set keySet()
* 将Map中所有的key存入一个Set集合后返回
* 遍历该Set集合等于遍历了所有的key
*/
Set<String> keySet = map.keySet();
for(String key: keySet){
System.out.println("key: "+key);
}
——————————————————————————————————————————————————————
2* 遍历每一组键值对
Entry是Map的内部类,每new一个Entry就相当于有一组键值对
/*
* 遍历每一组键值对
* Map中每一组键值对都是用一个Entry的实例保存的,Entry是Map的内部类
* Set entrySet()
* 该方法会将Map中每组键值对(若干Entry实例)存入一个Set集合后返回
*/
Set<Entry<String ,Integer>> entrySet = map.entrySet();
for(Entry<String,Integer> e : entrySet){
String key = e.getKey();
Integer value = e.getValue();
System.out.println(key+":"+value);
}
Set的泛型就是Entry,然而Entry自己也有泛型!!如Entry<k,v>
Map.Entry<K,V> 常用方法,用来获取Entry里面的Key和Value,分别是:
getKey()
getValue()
————————————————————————————————————————————————
3:遍历所有的value(相对不常用)
values
Collection<V> values()
返回此映射中包含的值的 Collection 视图。该 collection 受映射支持,所以对映射的更改可在此 collection 中反映出来,反之亦然。如果对该 collection 进行迭代的同时修改了映射(通过迭代器自己的 remove 操作除外),则迭代结果是不确定的。collection 支持元素移除,通过 Iterator.remove、Collection.remove、removeAll、retainAll 和 clear 操作可从映射中移除相应的映射关系。它不支持 add 或 addAll 操作。
返回:
此映射中包含的值的 collection 视图
——————————————————————————————————————————————————
/*
* 遍历所有的value
* Collection values()
* 该方法会将所有的value存入一个集合后返回
*/
Collection<Integer> values = map.values();
for(Integer value : values){
System.out.println("value"+value);
}
%…………%……%……%……%……%……%……%……%……%……%……%……%……%……%……%……%……%……
为毛用集合接口Collection????????????????????????????????????????
答:因为
values方法 返回此映射中包含的值的 Collection 视图。所以接收的也当然是Collection啦
map的方法有keySet和entrySet和values方法,第一个和第二个都返回Set视图,当然
其中第一个的keySet返回此映射中包含的键的 Set 视图,而entrySet返回此映射中包含的映射关系的 Set 视图。
最后,values方法 返回此映射中包含的值的 Collection 视图。所以接收的也当然是Collection啦
————————————————————————————————————————————————
散列算法:相当于酒店的前台,根据身份证查询,也就是key。key通过hashcode()计算存放位置,get的时候
调用hashcode查询到指定位置。
缺陷:使用不当,效率也低。应该避免一个空间存多个值,如保证身份证号不同
尽量避免两个hashcode一样
相同的key就是替换value,equals比较的是true就是一样
hashcode一样,key比较的却不是true,也就是身份证号hashcode一样,但不是同一张身份证key。
********************就是Map中尽量避免链表?????????????????????????
理解:链表不是连续存储的,而是位置不固定的,一个元素只链接上一个和下一个,其他是不知道的,找也是根据算法值找数据的。
hashmap存储时不确定具体位置,而是通过算法给出hashcode值,算出存储位置,而获取的时候也是根据hashcode直接获取key从而返回value。
1.2. HashMap
1.2.1. hash表原理
HashMap是Map的一个常用的子类实现。其实使用散列算法实现的。
HashMap内部维护着一个散列数组(就是一个存放元素的数组),我们称其为散列桶,而当我们向HashMap中存入一组键值对时,HashMap首先获取key这个对象的hashcode()方法的返回值,然后使用该值进行一个散列算法,得出一个数字,这个数字就是这组键值对要存入散列数组中的下标位置。
那么得知了下标位置后,HashMap还会查看散列数组当前位置是否包含该元素。(这里要注意的是,散列数组中每个元素并非是直接存储键值对的,而是存入了一个链表,这个链表中的每个节点才是真实保存这组键值对的。)检查是否包含该元素时根据当前要存入的key在当前散列数组对应位置中的链表里是否已经包含这个key,若不包含则将这组键值对存入链表,否则就替换value。
那么在获取元素时,HashMap同样先根据key的hashcode值进行散列算法,找到它在散列数组中的位置,然后遍历该位置的链表,找到该key所对应的value之后返回。
看到这里可能有个疑问,链表中应该只能存入一个元素,那么HashMap是如何将key-value存入链表的某个节点的呢?实际上,HashMap会将每组键值对封装为一个Entry的实例,然后将该实例存入链表。
1.2.2. hashcode方法
HashMap的存取是依赖于key的hashcode方法的返回值的,而hashcode方法实际上是在Object中定义的。其定义如下:
int hashCode()
重写一个类的hashcode()方法有以下注意事项:
1、若一个类重写了equals方法,那么就应当重写hashcode()方法。
2、若两个对象的equals方法比较为true,那么它们应当具有相同的hashcode值。
3、对于同一个对象而言,在内容没有发生改变的情况下,多次调用hashCode()方法应当总是返回相同的值。
4、对于两个对象equals比较为false的,并不要求其hashcode值一定不同,但是应尽量保证不同,这样可以提高散列表性能。
——————————————————————————————————————————————————————————
1.2.3. 装载因子及HashMap优化
在散列表中有一下名词需要了解:
Capacity:容量, hash表里bucket(桶)的数量, 也就是散列数组大小.
Initial capacity:初始容量, 创建hash表的时 初始bucket的数量, 默认构建容量是16个元素. 也可以使用特定容量.
Size : 大小, 当前散列表中存储数据的数量.
Load factor:加载因子, 默认值0.75(就是75%), 向散列表增加数据时如果 size/capacity 的值大于Load factor则发生扩容并且重新散列(rehash).
那么当加载因子较小时候散列查找性能会提高, 同时也浪费了散列桶空间容量. 0.75是性能和空间相对平衡结果. 在创建散列表时候指定合理容量, 从而可以减少rehash提高性能。
————————————————————————————————————————————————————————
1.3. 有序Map
1.3.1. LinkedHashMap实现有序的Map
Map 接口的哈希表和链表实现,具有可预知的迭代顺序。此实现与 HashMap 的不同之处在于,LinkedHashMap维护着一个双向循环链表。此链表定义了迭代顺序,该迭代顺序通常就是将存放元素的顺序。
需要注意的是,如果在Map中重新存入已有的key,那么key的位置会不会发生改变,只是将value值替换。
————————————————————————————————————————————————————————————
HashMap Hash算法
Map的内部其实就是一个数组,但是比数组快
重写equals的同时也要重写hashcode
具体的重写eclipse可以完成,但是面试中常用
Capacity :容量,hash表里bucket(桶)的数量
hashcode应该是无论调用多少遍都是不变的
hashcode的Initial Capacity容量默认就是16 也就是16个元素
Size 当前散列表中存储数据的数量
load factor 0.75 元素个数不能超过3/4,也就是12个元素,一旦超过就扩容,每次都是翻倍扩容 16->32-->64
Map是根据散列算法算的。哪怕16个空间也有可能一个空间存多个元素
如3*5=15 但是1*15也是15,hashcode算出都是15,结果就存同一个位置了
每次扩容之后之前的都要重新计算hashcode,重新存放位置
如存放75万条数据,最好一次性估算容量
Map<String,Integer> map = new HashMap<String ,Integer>(10000000000); //自定义一百万个元素大小
Map在大数据中常用。根据一次算法告诉你位置。
——————————————————————————————————————————————————
public class SortListDemo3 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("苍老师");
list.add("范老");
list.add("小泽老师");
System.out.println(list);
MyComparator com = new MyComparator();
/*
* 比较大小的规则。
*
* 该sort方法有个明显的缺点:
* 当我们在使用sort方法排序时,该方法要求集合元素必须实现comparable接口,
* 这就导致sort方法对我们的类有较强的侵入性
*
*/
//Collections.sort(list,com);
//只用一次的话就用匿名内部类的形式
Collections.sort(list,new Comparator<String>(){
public int compare(String o1,String o2){
return o1.length()-o2.length();
}
});
System.out.println(list);
}
}
/**
* 提供额外比较器的好处1:不要求元素实现comparable接口(侵入性:也就是代价高昂。
* 如Point类,只是用sort一句,但是却需要实现comparable接口,然后还要重写个接口,如果将来不需要了,这些代码就没用了)
*面向接口开发,而不应该面向类开发。调用者和被调用者的关系完全模块化,涉及到框架,比如Spring框架
*
*重载的sort方法有两个好处:
*1,由于提供了额外的比较器,所以不要求集合元素必须实现Comparable接口,这就没有侵入性
*2:有些元素自身已经实现了Comparable接口,并且定义了比较规则,但是又不满足这里排序需求,
*也可以使用额外的比较器来定义比较规则,从而满足排序需求
*/
class MyComparator implements Comparator<String>{
public int compare(String o1,String o2){
return o1.length()-o2.length();
}
}
——————————————————————————————————————————————
day06
file操作
通常写相对当前目录,不写绝对路径
File file = new File("./demo.txt");
Linux只认/,Windows也认,但Windows用\
实际应该:("."+File.separator+"demo.txt");
file获取信息属性的方法:
String getName()
long length()
long lastModified() //获取的是毫秒值,需要用Date转换
boolean canRead()
boolean canWrite()
boolean isHidden()
file不能访问文件内容
file的创建方法:
createNewFile()
file文件删除方法:delete()
目录创建:
File dir = new File("demo");
dir.mkdir()
创建多级目录:
File dir = new File("a/b/c/d/e");
dir.mkdirs()
-————————————————————————————————————————
获取目录下的所有子项需要用用listFiles()方法,并且获取给File类型的数组,通过遍历输出
File dir = new File(".");
if(dir.isDirectory()){
/*
* File[] listFiles()
* 该方法可以获取当前目录下的所有子项
*
*/
File[] subs = dir.listFiles();
for(File sub : subs){
if(sub.isFile()){
System.out.print("文件: ");
}
else if(sub.isDirectory()){
System.out.print("目录: ");
}
System.out.println(sub.getName());
}
————————————————————————————————————————————
file的过滤:
获取子项的listFile方法里面new 一个过滤方法FileFilter
但是FileFilter是一个接口,只有一个boolean accept方法,需要重写该方法
以下用匿名内部类实现,因为只用一次
File dir = new File(".");
File [] subs = dir.listFiles(new FileFilter(){
//重写FileFilter的boolean accept方法
public boolean accept(File file){
System.out.println("正在过滤:"+file.getName());
//设置过滤规则
//return file.isFile();
//过滤以"."开头的文件。
//return file.getName().startsWith(".");
return file.getName().endsWith("txt");
}
});
//subs存的是经过过滤的的数据,遍历输出
for(File sub: subs){
System.out.println(sub.getName());
}
——————————————————————————————————————————
删除多级目录,day06的test.java,实用案例。
使用递归
当发现每次都要把所有的方法都走一遍,就用递归
public static void delete(File f){
if(f.isDirectory()){
//先将其所有子项删除
File[] subs = f.listFiles();
//自己本身就是delete方法,因此调用自己的delete方法,把子项传进去
for(File sub: subs){
//递归调用,自己的内部调用自己
delete(sub);
}
}f.delete();
}
一定要有条件判断,不能百分之百干,否则就是死循环。
不能进太多层,否则很消耗资源
——————————————————————————————————————————
关于递归的电影:比如,盗梦空间
listFile还有一个重载方法,获取
listFiles(FileFilter filter),其中FileFilter是接口,如果要用也要实现然后重写,跟
comparable与comparator一样。使用匿名内部类
——————————————————————————————————————————————————
/**
* 1:编写一段代码,不超过20,要求完成从1+2+3一直加到100,并输出最终结果
* 在代码中不能出现for,while关键字
*
*
* 2:有20块钱,买汽水,1块钱一瓶,然后3个瓶盖可以换一瓶汽水,两个空瓶也可以换一瓶汽水,总共能买多少瓶汽水?
* 20P ->20/2=10P,20P--> 30/3=10P->
*
*/
有20块钱,买汽水,1块钱一瓶,然后3个瓶盖可以换一瓶汽水,两个空瓶也可以换一瓶汽水
20C->------------> ->10C
->>>10P |->5P
20P-> 10C--> ->10E|
20E-> 10P -> ->5C |->5C
10E -> 5P-
->5E
——————————————————————————————————————————————
文件操作RandomAccessFile
/*
* RandomAccessFile两个常用的构造方法:
* RandomAccessFile(File f,String mode)
* RandomAccessFile(String path,String mode)
*
* mode是创建模式,常用的对应两个字符串:
* “rw”读写模式,对文件进行读取也可以写入
* “r” 只读模式,仅对文件进行读取操作
*/
//可以直接提供创建文件,不需要另外File file = new File("raf.dat");
RandomAccessFile raf = new RandomAccessFile("raf.dat","rw");
RandomAccessFile raf = new RandomAccessFile("raf.dat","rw");
/*
* void write(int d)
* 向文件中写入一个字节,写的是给定的int值对应的二进制的“低八位“
* vvvvvvvv
* 00000000 00000000 00000000 00000001
* 不能直接操作
*/
//1写的不是1,而是1的二进制
raf.write(1);
System.out.println("写入完毕");
raf.write(97);
System.out.println("写入完毕");
raf.write(-1); //-1如果只有低8位存进去,也就00000...0000.11111111也就是255.如果存32位4个字节才是-1
System.out.println("写入完毕");
raf.close();
————————————————————————————————————————————
int read(byte[] b)
将最多 b.length 个数据字节从此文件读入 byte 数组。
int read(byte[] b, int off, int len)
将最多 len 个数据字节从此文件读入 byte 数组
如:
byte[] data = new byte[32]; //现在data数组长度32字节,内容为0
raf.read(data); //通过read方法,现在data已经装了32字节数据。
read(byte[] b);方法返回的是int类型的已经读取数据的数组长度
return String(data,"UTF-8");//返回的data已经是数组长度的数据
——————————————————————————————————
RandomAccessFile raf = new RandomAccessFile("raf.dat","r");
/*
* 从文件中指针当前位置读取一个字节,并以十进制的int值返回。若返回值是-1,-1是一个字节永远无法
* 表示的数,表示-1需要用到4个字节,32位,前面都是111... 则读取到了文件末尾了
*/
write和read一次只能操作一个字节
int read 一次只能操作一个字节
int read(byte[] d) 尝试最多读取给定数组lenght的字节并存入数组
raf的读的方法:
read
readLine
readInt
readShort
readDouble
readLong
readByte
但是没有readString方法,可以自定义个readString的方法
public static String readString(RandomAccessFile raf , int len) throws IOException{
byte[] data = new byte[len];
raf.read(data);
return new String(data,"UTF-8");
}
-----------------------------------------------------------------------------------------------------
day07 AM
————————————————————
int max = Integer.MAX_VALUE;
int的长度是4个字节,long是8个字节。然而write只写低8位
/* vvvvvvvv
* 01111111 11111111 11111111 11111111
*
* 00000000 00000000 00000000 00000001
*/
因此造成int前面的3的没写入。因此需要通过移位每次把8位写进去。如果是Long型,则需要8次,int的4次,如下:
raf.writeInt
raf.write(max>>>24);
raf.write(max>>>16);
raf.write(max>>>8);
raf.write(max);
/*
* writeInt方法一次性将给定的int值的4个字节全部写出,等同于上面4句,原理是一样的,详见源代码
*/
raf.writeInt(max);
raf.writeLong(123L);
raf.writeDouble(123.123)
-————————————————————————————————————————————
System.out.println("pos"+raf.getFilePointer());
把指针移回0位置
raf.seek(0)
但是也只读到低8位的值,也就127,而不是int的值。因此需要左移。readInt已经实现了
RandomAccessFile是基于指针的,可以随意的读取指定位置,也是可以返回位置读取
流跟水管一样,一次性读完,需要再读取再重新读取
RandomAccessFile是可以读也可以写,但是raf写入的是二进制。因为写入的值字节流,因此也不需要像高级流那样指定编码
——————————————————————————————————————————————————————
比如获取int和long和double的最大值:
RandomAccessFile raf = new RandomAccessFile("raf.dat","rw");
int maxint = Integer.MAX_VALUE;
long maxlong = Long.MAX_VALUE;
double maxdouble = Double.MAX_VALUE;
raf.writeInt(maxint);
raf.writeLong(maxlong);
raf.writeDouble(maxdouble);
System.out.println("Ding!Write Finished!");
//写完int4个字节和long8个字节后FilePointer实际已经在第九个字节位置了,需要把指针移回0位置读取
raf.seek(0);
System.out.println("MaxInt: "+raf.readInt());
System.out.println("MaxLong: "+raf.readLong());
System.out.println("MaxDouble: "+raf.readDouble());
raf如何将用户输入的每一行字符串写入到文件中呢?虽然有readLine方法,但是没有writeLine方法啊?
解决:参考作业day06的Test09
将输入的line字符串调用getBytes()给转成字节流,因为raf的write只能写入int类型
raf.write(line.getBytes());
——————————————————————————————————————————————
day07 PM
——————————————————
输入流:读
输出流: 写
参照物:程序
节点流,低级流:也就是从数据源搬运数据的,必然会有的
处理流,高级流: 处理数据,简化操作的,可选的
IS 所有的输入流的父类,
OS 所有的输出流的父类,
void write(int d)
void write(byte[] d)
若指定的文件已经包含内容,那么当使用FOS对其写入数据时,会将该文件中原有数据全部清除。但是有支持添加参数的重载方法:
FileOutputStream(File file, boolean append)
创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
/**
* java.io.FileOutputStream
* 文件输出流
*
* 流根据功能分为:
* 输入流:输入流是用来读取数据的
* 输出流:输出流是用来写出数据的
*
* InputStream是所有字节输入流的父类
* OutputStream是所有字节输出流的父类
*
* 流还分为字节流(低级流)与处理流(高级流)
* 低级流:数据源明确,真实负责“搬运数据”的流读写一定要有低级流
* 高级流:不能独立存在,没有意义。它是用来处理其他流的,通过处理其他流来简化我们的读写操作
*
* 文件输出流FileOutputStream是一个低级流,作用是向文件中写出数据
*
*/
流要么清空了重写,还有支持追加模式
FileOutputStream fos = new FileOutputStream("fos.txt",true);
多了个true表示追加
/*
* FileOutputStream支持第二个参数是一个boolean值,该值为true时,为追加操作。
* 那么通过该流写出的内容会追加到文件末尾,否则就是覆盖写操作,即先将该文件原有数据全部删除,再开始本次流
* 写入的内容
* 一个参数的构造方法就是覆盖写操作
*/
————————————————————————————————————————————
FileInputStream fis = new FileInputStream("fos.txt");
byte[] data = new byte[100];
int len = fis.read(data);
String str = new String(data,0,len,"UTF-8");
高级流不仅可以处理低级流,也可以处理其他高级流。高级流的功能是可以叠加的
FileInputStream fis = new FileInputStream("demo.txt);
InputStreamReader isr = new InputStreamReader(fis);//这里可以指定编码
BufferedReader br = new BufferedReader(isr);
所有高级流都一样,只需要关闭高级流即可,因为其会自动先把下一级的关闭
FileInputStream fis = new FileInputStream ("tts.zip");
FileOutputStream fos = new FileOutputStream("tts2_copy.zip");
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);
也还是比上面的10k的数组形式的慢,因为只有8192字节。实际也是一次读一组,放到缓冲区先
——————————————————————————————————————————————————————
缓冲输出流的注意事项:
假如输入的字符没装满Buffered的长度,Buffered是先不写的,除非调用flush或者在关流的close之前写出。
用了Buffered,记得要调用flush(),如果频繁调用也会增加写入次数,耗费资源,看需求,是需要及时性还是效率
/*
* flush方法会强制将缓冲流缓冲区中已缓冲的数据一次性写出
*/
——————————————————————————————————————
对象流
只要想往文件写就必须用到低级流
接口有
1,约束的接口——常见的有约束行为,需要重写的
2,签名接口——比如Serializable接口
public class Person implements Serializable{多了个方法,只是方法不需要自己实现
————————————————————————————————————————————————
FileOutputStream fos = new FileOutputStream("person.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
/*
* writeObject方法要求写出的对象必须实现序列化接口。否则抛出异常
*/
oos.writeObject(p);
System.out.println("Ding!");
oos.close();
————————————————————————————————————————————
写进的Person.obj文件是二进制的,也比一半字符的大小大,因为需要保存每个字符串的类型等等信息。
————————————————————————————————————————————
OIS:
不是什么都能读成对象
/**
* java.io.ObjectInputStream
* 对象输入流,是一个高级流,作用是可以读取一组字节(必须是ObjectOutputStream将一个对象写出的一组字节)
* 并还原为对象
*
*/
————————————————————————————————————————————
FileInputStream fis = new FileInputStream("person.obj");
ObjectInputStream ois = new ObjectInputStream (fis);
//读取出来的也是还原成object对象,需要强转
Person p = (Person)ois.readObject();
System.out.println(p);
__________________________________________________________________________________________________
transient
如果某些信息不需要保存,可以在声明的时候,private修饰的后面加上transient关键字。当然
再次读取也是没有的
/*
* transient关键字修饰的JAVA 属性在进行对象序列化时会被忽略,将不需要的属性值忽略可以达到对象
* “瘦身”的目的,减少资源消耗。
*/
private transient List<String> otherInfo;
——————————————————————————————————————————————
/*
* writeObject方法要求写出的对象必须实现序列化接口。否则抛出异常
*
* 该方法首先是oos将给定对象转换成了一组字节,然后再通过fos将这组字节写入到文件person.obj中
* 这里经历的两个操作都有专业名词:
* 将对象转换为一组字节的过程称之为:对象序列化
* 将字节写入到磁盘的过程称为:持久化
*
*/
oos.writeObject(p)
DAO 数据访问对象 Data Access Object
——————————————————————————————————————————————
public class Person implements Serializable{
/**
* 根据当前类的属性算出来的一个版本号,版本号也会保存到写入的文件中。
* 只要该了属性都会重新生成版本号,
* 如果和读取的文件的版本号比对不一致了就会抛出无效类异常
*/
private static final long serialVersionUID = 1L;
private String name;
private int age;
private String gender;
版本号不对,反序列一定失败的。
版本号可以自定义的
还原,如果原来有的,现在也有,就还原;如果原来有的,现在没有,比如age改成salary就忽略;
如果原来没有,现在有的,比如文件没有salary,但是现在有,就使用默认值,int的0
* 当一个类实现类serializable接口后,应定义一个常量serialVersionUID,
* 这个是序列化版本号,该值直接决定当前类实例在进行反序列化时的成功与否。
* 当使用ObjectInputStream对一个已经序列化的对象进行反序列化时,会首先检查该对象
* 的版本号与当前类的版本是否一致,不一致则直接反序列化失败。
* 若一致则可以还原。那么若当前类的结构发生了改变,则在进行反序列化时启用兼容模式,即
* 原有的属性现在依然有的则还原,原有属性现在没有了则忽略,现有新的属性则使用默认值
*
* 若不指定版本号,编译器会在编译当前类时根据当前类结构生成一个版本号,那么当前类结构只要
* 发生改变,版本号一定改变,那么反序列化就一定失败了
_____________---------------------------------------------------------------_________________________
如果父类的返回值类型是基本类型或者void的,子类必须与父类保持一致
字节流:最底层最基本的,binary的
字符
所有写入操作都得转成字节,用getBytes ,要转回字符串用toString
所有的字符流都是高级流
——————————————————————————————————————————————
day08 am December 1,2016
**
*
*
V
只要是Reader结尾的都是字符流
Read常用方法:
int read()一次都是读一个字符,而不是低八位,返回的int值“低16位”表示的字符
int read(char[] chs)
InputStreamReader
OutputStreamWriter
void write(char[] cbuf, int off, int len)
写入字符数组的某一部分。
void write(int c)
写入单个字符。
void write(String str, int off, int len)
————————————————————————————————————————————
大部分只能套字符流,如上,BufferedInputStream套FileInputStream,高级套低级流实现。
/**
* 字符流
* 字符流是以字符为单位进行读写数据的,所以字符流也仅能读写文本数据。字符流都是高级流,本质上字符流读写字符是会自动
* 与字节进行转换进行读写,所以底层还是要读写字节的
*
* Reader是一个抽象类,所有字符输入流都继承它
* Writer是一个抽象类,所有字符输出流都继承它
* 它们规定了所有字符流都应当具备的读写字符的方法
*
* 转换流
* InputStreamReader:该流是Reader的实现类,很多字符流都只能处理其他字符流,但是低级流都是字节流,所以就需要先将字节流转换为字符流才可以被其他高级流使用,这时候常用转换流进行转换
* OutputStreamWriter:Writer的实现类,作用是可以将字节输出流转换为字符输出流
*
* 转换流还有一个作用是可以指定读写字符的字符集
*
*/
public class OSWDemo {.......
转换流相当于Adapter,比如:不同数据线用适配器的例子
FileInputStream --> BufferedInputStream --> BufferedReader
--> InputStreamReader -->
BufferedReader in=
new BufferedReader(new InputStreamReader(System.in));
这句跟视频的代码一样
-————————————————————————————————————
FileInputStream fis =
new FileInputStream("src"+File.separator+"day08"+File.separator+"BRDemo.java");
InputStreamReader isr = new InputStreamReader(fis);
/*
* 缓冲字符输入流只能处理其他字符输入流,所以要先用InputStreamReader将字节流转换为字符流
*/
BufferedReader br = new BufferedReader(isr);
/*
* String readLine()
* 该方法会连续读取若干字符,直到读取到换行符为止,然后将换行符之前的所有
* 字符组成一个字符串返回。
* 注意:
* 返回的字符串中不含有最后的换行符
* 当返回值为null时,说明读取到文件末尾
*/
String line = null;
while((line=br.readLine())!=null){
System.out.println(line);
}
br.close();
((((((((((((((((((((((((((((((((((((((((((((((()))))))))))))))))))))))))))))))))))
FileOutputStream --> BufferedOutputStream --> BufferedWriter
||
||
v
OutputStreamReader
||
||
v
PrintWriter
以前只能在getBytes时指定字符集,
OutputStreamWriter支持第二个参数,指定字符集
FileInputStream 只能一个字符一个字符读,或者一次读一个数组
——————————————————————————————————————)))
FileInputStream fis = new FileInputStream("osw.txt");
InputStreamReader isr =new InputStreamReader(fis,"GBK");
//读回来的是一个字符,需要转成char,构造方法中也支持第二个参数
int d = -1;
while((d=isr.read())!=-1){
System.out.println((char)d);
}
isr.close();
——————————————————————————————————————))))
valueOf()把其他字符转成字符串
————————————————————————————————————————
//FileInputStream 只能一个字符一个字符读,或者一次读一个数组
char[] data = new char[100];
int len = -1;
len = isr.read(data);
String str = String.valueOf(data,0,len);
System.out.println(str);
isr.close();
————————————————————————————————————)))))))
/**
* 缓冲字符流
* 特点是可以按行读写字符串
*
* java.io.BufferedWriter是缓冲字符输出流
* java.io.BufferedReader是具有自动行刷新的缓冲字符输出流,其更常用,而且
* 内部默认嵌套BufferedWriter作为缓冲
*
*/
public class PWDemo1{.......
/*
* PW提供了方便写文件的构造方法
* PrintWriter(File file)
* PrintWriter(String path)
* 并且上述两个构造方法还支持第二个参数取指定charSet
*/
PrintWriter(File file, String csn)
创建具有指定文件和字符集且不带自动刷行新的新 PrintWriter。
PrintWriter(String fileName, String csn)
创建具有指定文件名称和字符集且不带自动行刷新的新 PrintWriter。
PrintWriter源代码:
public PrintWriter(OutputStream out, boolean autoFlush) {
this(new BufferedWriter(new OutputStreamWriter(out)), autoFlush);
______________________________________________________________________________________________
PrintWriter pw = new PrintWriter("pw.txt");
或者自定字符集:
PrintWriter pw = new PrintWriter("pw.txt",“GBK“);
PrintWriter不需要调用write()方法,只需要pw.println();
一次写一行,写完一句换行继续写下一句
对流做操作不支持第二个参数定义字符集,操作文件才可以,如下
PrintWriter pw = new PrintWriter(fos);
但是可以用OutputStreamWriter osw = OutputStreamWriter(fos,"gbk")
再用PrintWriter pw = new PrintWriter(osw);
————————————————————————————————————————————
//对流做操作不支持第二个参数定义字符集
//PrintWriter pw = new PrintWriter(fos,"gbk");
//但是可以通过另一个高级流转好了字符集再用
OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk");
PrintWriter pw = new PrintWriter(osw);
pw.println("PrintWriter处理其他流");
pw.println("提供的构造方法既可以传入字节流也可以传入字符流");
————————————————————————————————————————————————————
public class Note {
FileOutputStream fos = new FileOutputStream(filename);
OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8");
/*
* 只要第一个参数是流,就支持第二个参数true,作为自动flush的开关,
* 当然要pw.println(line)而不能是pw.print(line),因为要换行才会flush
*line是用户输入的字符串内容
* 当PrintWriter处理的是一个流时,则支持第二个参数,该参数为boolean,若为true,
* 则具有自动行刷新,即,每次使用println方法写出的字符串后会自动flush
*/
PrintWriter pw = new PrintWriter(osw,true);
System.out.println("请输入内容:");
String line=null;
————————————————————————————————————————————————
InputStreamReader isr = new InputStreamReader(fis);
/*
* 缓冲字符输入流只能处理其他字符输入流,所以要先用InputStreamReader将字节流转换为字符流
*/
BufferedReader br = new BufferedReader(isr);
/*
* String readLine()
* 该方法会连续读取若干字符,直到读取到换行符为止,然后将换行符之前的所有
* 字符组成一个字符串返回。
* 注意:
* 返回的字符串中不含有最后的换行符
* 当返回值为null时,说明读取到文件末尾
*/
String line = null;
while((line=br.readLine())!=null){
System.out.println(line);
}
————————————————————————————————————————————————
day08 pm
*
*
Exception
所有异常都继承Throwable Error虚拟机级别的错误
Throwable
直接已知子类:
Error, Exception
Exception 相当于自身的问题。
Error大级别状况,非自身能处理
try{可能出异常的代码}
————————————————————————————————————————————————
异常也是一个封装类
一旦try中一行出错了try中下面的就不会执行了
try不出错catch就不会执行
catch可以写多个,捕获异常
为了预防未知的错误,最后应该都抛一个父类异常
catch(Exception e){
}
子类异常在上,父类异常在最后
但要注意继承关系,如果父类的异常Exception放最上面,那子类的其他异常就不会执行了。
————————————————————————————————————————————————————
public class ExceptionDemo3 {
public static void main(String[] args) {
FileOutputStream fos = null;
try{
fos = new FileOutputStream("fos.dat");
fos.write(1);
}
catch(Exception e){
System.out.println("读写出异常鸟");
}
finally{
if(fos!=null){
try{
fos.close();
}
catch(Exception e){
System.out.println("究竟哪里错了??");
}
}
}
System.out.println("Ding");
}
}
————————————————————————————————————————————————————————
符合语法,不符合业务逻辑,抛出异常
不是自己的问题,让执行的下一层处理异常。举例:托人介绍对象。责任制,自己的问题自己处理,不是自己的给谁负责的捕获处理
throws在方法上声明可能会报一些错,举例:“丑话说前边”
只要不是RuntimeException的及其子类,必须处理它
1,自己try catch
2,方法上继续throws
永远不要在main方法throws,不负责任。
——————————————————————————————————————————————————————
public static void main(String[] args) {
Person p = new Person();
try{
/*
* 当调用一个含有throws声明异常抛出的方法时,编译器要求必须处理该异常
* 处理方式有两种:
* 1,使用try-catch捕获并处理
* 2,在当前方法上继续使用throws声明该异常的抛出
* 不要在main方法上使用throws
*/
p.setAge(10000);
}
catch(Exception e){
System.out.println("年龄非法");
}
————————————————————————————————————————
throw是一个动作,抛异常
throws是一个声明,告知被调用的该方法可能会有异常
区别:throw和throws
前者是动作,抛出,定义在方法内。后者是声明,定义在方法体外
子类继承父类需要重写父类方法,但是如果父类抛了异常呢?
——————————————————————————————————————————————————————
public class ExceptionDemo5 {
public void dosome() throws IOException,AWTException{
}
}
class Son extends ExceptionDemo5{
// //不再抛出任何异常
// public void dosome(){
//
// }
//
// //只抛出父类方法抛出的部分异常
// public void dosome()throws IOException{
//
// }
// //抛出父类方法抛出异常的子类型异常
// public void dosome() throws FileNotFoundException{
//
// }
// //不可以抛出额外的异常
// public void dosome() throws SQLException{
//
// }
//
// //不可以抛出父类方法抛出异常的父类型异常
// public void dosome() throws Exception{
//
// }
//
——————————————————————————————————————————————————————
检查异常:如果方法抛异常既不try catch也不throws,少一个编译都不通过
非检查异常:如果是RuntimeException,编译器不报错,但出错也会导致程序中断
RuntimeException是属于常识型异常
RuntimeException的子类:空指针异常,下标越界....
自变量equals(变量)
printStackTrace输出执行堆栈信息,也就是编译运行提示的
catch(Exception e){
e.printStackTrace();
————————————————————————————————————————————
getMessage和getCause实际运用中较少,后者大项目多见
——————————————————————————————————————————
自定义异常,通常不继承RuntimeException,一般继承Exception
静态方法是静态块初始化的!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
有native关键值表示是C语言的方法
JNI:JAVA NATIVE INTERFACE
java本地接口,用来调用c语言
Android也可以用C语言写,苹果也可以。用java调用c的口。用NDK
————————————————————————————————————————————————————————
day09 am
Thread
*
*
*
*
*
并发:微观上走走停停,宏观上都在运行
IO Block通过动作驱动
sleep Block周期性工作,比如飞机大战的30毫秒计时刷新
wait Block
剥夺出让yield
每个线程class都需要继承Thread
//要实现run方法,run方法无参数,具体动作写到run方法里面
class MyThread1 extends Thread{}
run方法会自动被调用的,只需要调用start方法即可
Thread t1 = new Thread();
Thread t2 = new Thread();
/*
* 启动线程要执行start方法,不能调用run方法,run方法是线程要执行的任务,当线程启动后
* 一旦被分配cpu时间片会自动执行自己的run方法
*/
t1.start();
t2.start();
————————————————————————————————————————————————
多线程没有先后问题,谁拿到cpu就运行
————————————————————————————————————————————
/**
* 第一种创建线程的方式有两个不足:
* 1,由于java是单继承的,所以导致当前类继承了Thread就不能再继承其他类。在实际开发中经常会出现继承冲突
* 2,由于线程内部重写了run方法并定义了线程的任务,这就导致该线程与其执行的任务有一个必然的耦合关系,不利于线程的重用
*/
/**
* 第二种创建线程的方式:单独定义线程任务
* 实现Runnable接口并重写run方法
*/
线程本身就可以实例化
————————————————————————————————————————————————
Runnable r1 = new MyRunnable1();
Runnable r2 = new MyRunnable2();
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
/*
* 线程用于并发执行任务,多个线程运行任务
* 相当于现实生活中多个人各做各的事情
*
* 线程有几点不可控:
* 线程何时运行,运行多久完全听从线程调度的同一安排
* 1:时间片长短线程不可控
* 2:时间片分配给哪个线程线程也不可控。
* 线程只能被动被分配时间片,不能主动索取
* 线程调度会尽可能均匀地将时间片分配给不同的线程,但是不保证“一人一次”
*/
t1.start();
t2.start();
————————————————————————————————————————————————————
也是用匿名内部类的方式创建
+++++++++++++++++++++++++++++++++++++++++++++++
new Thread(
//相当于new Thread(new ThreadDemo$3).start();
//为何这里用Runnable,不是实现吗?匿名的是Runnable的子类,new的其实是Runnable的一个子类
new Runnable(){
public void run(){
for(int i=0;i<1000;i++){
System.out.println("老乡开门,我们不拿一针一线!");
}
}
}
).start();
——————————————————————————————————————————————
线程有一个构造方法,可以指定线程名字,但其实非特别的线程没必要指定名字
/*
* 获取运行main方法的线程
*/
Thread main = Thread.currentThread();
System.out.println("运行main方法的线程是:"+main);
dosome();
Thread t = new Thread("自定义线程"){
public void run(){
Thread t = Thread.currentThread();
System.out.println("自定义线程是:"+t);
dosome();
}
};
t.start();
——————————————————————————————————————————————
day08 PM
Thread提供了获取线程信息的方法:
long getId()
String getName()
int getPriority()
boolean isAlive()
boolean isDaemon() // 测试线程是否处于活动状态。
boolean isInterrupted()
/**
* 线程优先级
* 优先级越高,被cpu分配的时间片越多。默认是5,最高10
* 线程优先级有10个等级,对应的数字是1-10
* 其中1是最低优先级,10是最高优先级
* 理论上,优先级越高的线程,大部分情况,并发量大更容易查看获取cpu时间片的次数越多,线程提供了常量对应了区间
* 范围的最高与最低和默认:
* Thread.MAX_PRIORITY:最高优先级,对应数字10
* Thread.MIN_PRIORITY:最低优先级,对应数字1
* Thread.NORM_PRIORITY:默认优先级,对应数字5
*/
————————————————————————————————————————————
/**
* sleep阻塞
* 线程提供了一个静态方法:
* static void sleep(long ms)
* 该方法可以导致调用该方法的线程进入阻塞状态指定的毫秒,当超时后线程会自动回到Runnable状态等待
* 分配时间片继续并发运行
* 通常使用sleep做周期性间隔使用
*/
跳秒。线程在Runnable中等待需要时间,累积刚好达到千毫秒也就是秒的跨越时刚跳过
void setDaemon(boolean)
守护线程的特点是,当进程中只剩下守护线程时,所有守护线程强制终止。
main肯定是前台线程
/**
* 守护线程,又称为后台线程
* 当一个进程中的所有前台线程都结束时,进程结束。
* 那么正在运行的后台线程会被强制结束
* 默认创建的线程都是前台线程,后台线程需要单独设置。
* 设置必须在调用start方法之前,调用setDaemon(true)
*/
————————————————————————————————————————————————————
方法执行完毕main最先结束,但是其他方法还在执行
进程结束与否是前台是否还有线程
static void yield()主动让出时间
暂停当前正在执行的线程对象,并执行其他线程。
——————————————————————————————————————————————————————
join方法。用于等待当前线程结束,该方法声明抛出InterruptedException
同步:有先后顺序的
异步:多个线程各做个的
同步代码块 sychronized关键字
————————————————————————————————————————————————————
想让谁阻塞,比如show要等到download执行完毕才执行,就在局部内部类里面使用download.join();
方法里的变量叫局部变量,方法里面的方法叫做局部内部类
注意:一个方法的局部内部类中要使用这个方法的其他变量,这个变量需要final。
比如这里的download需要final,1.8才可以不加,之前的版本都要加final
join还需要捕获InterruptedException
——————————————————————————————————————————————
/**
* join方法
* 线程的join方法可以做到线程同步运行
*
* join方法会将调用该方法的线程置于阻塞状态,直到其等待的线程执行完毕才会解除阻塞继续运行
*
* 同步:有先后顺序的执行代码为同步执行
* 异步:多线程并发运行通常就是异步执行,各执行各的互相没有牵制
* 线程同步:指的就是线程运行方式从各执行各的变为有先后顺序的执行
*
*/
——————————————————————————————————————————————————
public class ThreadDemo9 {
//表示图片是否下载完毕
public static boolean isFinish = false;
public static void main(String[] args) {
/*
* 当一个方法中的局部内部类中想引用该方法的其他局部变量,那么该变量必须是final的
* 这是因为jvm虚拟机内存分配问题所导致的,java 8.0之后没有此问题了
*/
final Thread download = new Thread(){
public void run(){
System.out.println("开始下载图片");
for(int i=1;i<=100;i++){
System.out.println("down"+i+"%");
try{
Thread.sleep(50);
}catch(InterruptedException e){
}
}
System.out.println("down:图片下载完毕");
isFinish = true;
}
};
Thread show = new Thread(){
public void run(){
/*
* 先等待download将图片下载完毕再尝试加载
*/
System.out.println("准备显示图片...");
try{
//匿名局部内部类
download.join();
if(!isFinish){
throw new RuntimeException("图片没有加载成功");
}
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("show:显示图片完毕");
}
};
download.start();
show.start();
————————————————————————————————————————————————————
线程同步。在调用返回值的过程中将其锁定,用完了其他方法才能使用,防止抢用
synchronized关键字
多个线程并发读写同一个临界资源时候会发生"线程并发安全问题“
常见的临界资源:
多线程共享实例变量
多线程共享静态公共变量
若想解决线程安全问题,需要将异步的操作变为同步操作。 何为同步?那么我们来对比看一下什么是同步什么异步。
所谓异步操作是指多线程并发的操作,相当于各干各的。
所谓同步操作是指有先后顺序的操作,相当于你干完我再干。
而java中有一个关键字名为:synchronized,该关键字是同步锁,用于将某段代码变为同步操作,从而解决线程并发安全问题
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
巩固:抽象类不能被实例化,只能被继承。
抽象的类绝对不能new对象。
new shape //编译错误;
Shape s;//句柄 正确。声明了引用类型的引用;
new Inter1(); //编译错误,接口不能被实例化
Inter1 o1; //正确
用static修饰的成员变量是属于对象的数据结构 错
用static修饰的成员变量是属于类
static成员变量存储在堆中 错
static成员变量存储在方法区中
子类的访问权限得大于或等于父类的
抽象方法也是一种数据类型(引用类型),所以可以作为类型数组
多态:
点出什么看引用类型,具体行为的实现要看对象
1.同一类型的引用,指向不同的对象时,有不同的实现
向上造型:
父类型的引用指向子类的对象
能造型成的类型:父类+所实现的接口
能点出什么看引用的类型
强制类型转换,成功的条件有2种:
引用所指向的对象,就是该类型;
引用所指向的对象,实现了该接口
不符合那两种条件则转换失败;
发生ClassCastException类型转换异常;
建议使用instanceof判断引用指向的对象是否是该类型
JVM的内存结构有方法区,堆,栈等。其中方法区用于保存类的各种信息;
栈用于存放程序运行过程当中所有的局部变量;
堆一般用于存储使用new关键字创建的对象。
类属于Java语言中引用类型的一种,不属于JVM的内存结构
--------------------------------------------------------------------------------------------------------------------------
线程安全API与非线程安全API
之前学习的API中就有设计为线程安全与非线程安全的类:
StringBuffer 是同步的 synchronized append();
StringBuilder 是java1.5后出的,为了提高性能损失安全性 不是同步的 append();
相对而言StringBuffer在处理上稍逊于StringBuilder,但是其是线程安全的。当不存在并发时首选应当使用StringBuilder。
同样的:
Vector 和 Hashtable 是线程安全的而ArrayList 和 HashMap则不是线程安全的。
对于集合而言,Collections提供了几个静态方法,可以将集合或Map转换为线程安全的:
例如:
Collections.synchronizedList() :获取线程安全的List集合
Collections.synchronizedMap():获取线程安全的Map
_______________________________________________________________________________________________________________
???????????????????????????????????????????????????????????????
RandomAccessFile 如何读取Object数据呢?比如day06的test的10,写完emp对象之后如何读取写入的内容??
解答:raf无论读写都是byte类型的。读取需要用到数组,raf调用数组引用,然后赋给长度len
最后new一个字符串用String,从0位置到len接收数组data。输出即可。如下:
RandomAccessFile reademp = new RandomAccessFile(filename,"rw");
byte[] data = new byte[1024];
int len=reademp.read(data);
System.out.println("实际读取到的字节量:"+len);
String str = new String(data,0,len);
System.out.println(str);
——————————————————————————————————————————————————————
day10 AM
synchronized
————————————————————————————————————————————————————————————
/**
* 当一个方法被sychronized修饰后,该方法称为“同步方法”,即:多个线程不能同时进到方法内部执行代码,这就导致
* 该方法从多线程异步执行变为同步执行,不存在抢的问题,所以解决了并发执行的安全问题
*
* 在方法上使用synchronized,那么上锁的对象就是当前方法所属对象,即:方法内部看到的this
*/
//加上sychronized关键字则只能有一个线程执行
public synchronized int getBeans (){
————————————————————————————————————————————————————
大括号:排队执行的代码
小括号:同一个类型。this也可以。一个方法的this就是方法的所属对象,就是调用的哪个方法就是哪个对象
class shop{
public void buy(){
try{
Thread t = Thread.currentThread();
System.out.println(t.getName()+" : 正在选衣服...");
Thread.sleep(1000);
/*
* 同步块
* 可以更精确地包含需要多线程同步执行的代码片段,这样可以在保证并发安全的前提下尽可能地提高并发效率。
* 但是需要注意,使用同步块时,指定的同步监视器对象(即圆括号中指定的对象,上锁的对象)必须保证
* 多个需要同步执行的线程看到的是同一个才行
*/
synchronized(this){
System.out.println(t.getName()+" : 正在试衣服");
Thread.sleep(1000);
}
System.out.println(t.getName()+" : 结帐离开");
}catch(Exception e){
}
}
多个线程操作同一个功能时,被操作的就上锁。如多个线程操作同一个集合,则把集合上锁
——————————————————————————————————————————————————————————
推荐写同步块,尽量不写到方法上。
/**
* 静态方法使用synchronized修饰后,该方法一定具有同步效果
* 静态方法锁的当前类的类对象,即Class的实例
* jvm加载每一个类的时候都会实例化一个class类的实例用于描述当前类的信息,并且在jvm内部只有一个class的实例
* 与当前类对应,锁的就是这个class的实例
*/
——————————————————————————————————————————————————————————
如果一个静态方法用了同步修饰,一定具有同步效果,跟对象没关系,跟类有关。再多new也是同步的。任何时候都是同步的
class Table{public void buy(){..}..} //buy方法不是静态,这里new两个,同步与否无所谓
final Table table1 = new Table();
final Table table1 = new Table();
——————————————————————————————————————————————————-
如果是静态:
class ...
public static void main(String[] args) {
//Class class1 = SyncDemo3.class;
//Method m = class1.getMethod("getXXX",null);
//m.invoke(obj,args); //java反射机制。可以不知道方法名,直接getXXX方法,java会操作调用
//final Foo f= new Foo();
//final Foo f2 = new Foo();
Thread t1 = new Thread(){
public void run(){
Foo.dosome(); //静态方法。类名.方法名即可
}
};
Thread t2 = new Thread(){
public void run(){
Foo.dosome(); //可以不new Foo,静态方法。类名.方法名即可
}
};
t1.start();
t2.start();
}
}
class Foo{
public synchronized static void dosome(){
...}}
——————————————————————————————————————————————————————
常见面试题:
同步锁:sync。修饰的方法
互斥锁:也是sync,修饰地方不一样。上锁的方法一样,但是访问的代码不一样。如同喘气和下咽不能同时。
具体看SyncDemo4
死锁:互相等对方先退,僵持状态 如day10 SyncDemo5
——————————————————————————————————————————————————————
day10 PM
——————————————————————————————————————————————
之前学习的API中就有设计为线程安全与非线程安全的类:
StringBuffer 是同步的 synchronized append();
StringBuilder 不是同步的 append(); java1.5以后加入的,非安全的
相对而言StringBuffer在处理上稍逊于StringBuilder,但是其是线程安全的。当不存在并发时首选应当使用StringBuilder。
Vetor向量 和 Hashtable是同步的
ArrayList 和 HashMap不是同步的
————————————————————————————————————————————————-----
同样的:
Vector 和 Hashtable 是线程安全的而ArrayList 和 HashMap则不是线程安全的。
对于集合而言,Collections提供了几个静态方法,可以将集合或Map转换为线程安全的:
例如:
Collections.synchronizedList() :获取线程安全的List集合
Collections.synchronizedSet(); HashSet用的就是HashMap
源代码就是这样的:
public HashSet() {
map = new HashMap<>();
}
(((((((((((((注意)))))))))))))))))
所有的集合不与Iterator做互斥。一个线程在查询集合,另一个也在用迭代器增删,不会互斥的,只能自己另外设置方法做互斥
/*
* 就算是线程安全的集合,也不与迭代器的遍历操作互斥。但是迭代器在遍历集合时
* 不能通过集合增删元素否则会抛出异常
* 所以在这种情况下需要自行维护互斥关系
*/
——————————————————————————————————————————————————————————
Collections.synchronizedMap():获取线程安全的Map
//Stack //HashSet就是HashMap
Set<String> set = new HashSet<String> ();
set.add("1");
set.add("2");
set.add("3");
System.out.println(set);
set = Collections.synchronizedSet(set); 需要接收值
System.out.println(set);
//HashSet就是HashMap
——————————————————————————————————————————————————————————
线程越多,cpu轮寻的时间就更长
1.1.2. 使用ExecutorService实现线程池
当一个程序中若创建大量线程,并在任务结束后销毁,会给系统带来过度消耗资源,以及过度切换线程的危险,从而可能导致系统崩溃。为此我们应使用线程池来解决这个问题。
ExecutorService是java提供的用于管理线程池的类。
线程池有两个主要作用:
控制线程数量
重用线程
线程池的概念:首先创建一些线程,它们的集合称为线程池,当服务器接受到一个客户请求后,就从线程池中取出一个空闲的线程为之服务,服务完后不关闭该线程,而是将该线程还回到线程池中。
在线程池的编程模式下,任务是提交给整个线程池,而不是直接交给某个线程,线程池在拿到任务后,它就在内部找有无空闲的线程,再把任务交给内部某个空闲的线程,任务是提交给整个线程池,一个线程同时只能执行一个任务,但可以同时向一个线程池提交多个任务
线程池有以下几种实现策略:
Executors.newCachedThreadPool()
//创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。固定时间内还没有被用就关闭线程
Executors.newFixedThreadPool(int nThreads)
//创建一个可重用固定线程集合的线程池,以共享的无界队列方式来运行这些线程。较常见
Executors.newScheduledThreadPool(int corePoolSize)
//创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
Executors.newSingleThreadExecutor()
//创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。光杆司令,解决特定问题
可以根据实际需求来使用某种线程池。例如,创建一个有固定线程数量的线程池:
——————————————————————————————————————————————————————
玩游戏的路径算法就是二叉树treeMap
————————————————————————————————————————————————————————————
双缓冲队列BlockingQueue。学过的LinkedList但不是安全的,做项目再单独讲。
网络游戏都是udp,即时性
课堂上不讲udp,只讲tcp,工作中常用tcp
port:
2000以内不要用常用的软件,1000以内是操作系统
0-65535也就是short的最大值
ServerSocket是被动创建的,只要有客户端连接就创建
——————————————————————————————————————
day11
怎么样才能让大家看得见呢?ServerHandler是内部类
内部类是否可以访问外部类的属性呢?可以
什么时候要把一个方法定义为private,就是不需要给外界知道的
MTA不可以补考 650一门课
——————————————————————————————————————————————————
day12 AM
xml Extensible Markup Language是独立于软件和硬件的信息传输工具
应用于 web 开发的许多方面,常用于简化数据的存储和共享。
简化数据共享
简化数据传输 子系:HTML
通常都用双引号
内部用到双引号时外部使用单引号
<id name='""""' age = "24"> </id>
必须且只能有一个根标签,也就是不被其他元素包含
转意字符:
< 小于
> 大于
&
' 表示单引号
" 表示双引号
CDATA段,里面的所有内容当作一整块文本数据
<![CDATA[ 全部都是文字 <name></name> <age></age> ]]>
注释:
<!-- 注释注释... -->
————————————————!!!!!!!!!!!!!!!!——————————————————
警告:
任何代码注意不要放到while(true)死循环下面,因为会报错:不可到达unreachable
注意注意!
int怎么转字符串??? 任何类型加上空字符串就是字符串啦 直接+" "
valueOf具体用法? 其他类型转字符串除了直接加空字符串外也可以用String.valueOf(被转的类型如int类型)
————————————————!!!!!!!!!!!!!!!!——————————————————
很多情况下都是使用非java官方的,第三方的jar包。经常出现的问题:某某某类找不到,就是因为jar包用了其他jar包,但是
打包发过来没把另一个jar包发过来。
告诉jvm jar包在哪,java会自动处理
maven可以连到apache的中央库,用到了些jar包
如果有下一级则信息写成属性,比如部门下有员工
SAX 也就是SIMPLE API FOR XML 在assemble设备常见。嵌入式。读一个告知一个
DOM 也就是DOCUMENT OBJECT MODEL文档对象模型。当场把xml全部读完。但是慢。所有xml都加载到内存里。如果文件比较大,内存有压力,解析的时间会比较长。java原生api没提供对dom的解析,因此需要导入jar包
阿里云不支持索引,只能在阿里云上搜索jar,下载到本地导入
外网需要去apache官网下载索引,几个g,才能在以后在本机索引,索引到了自动下载
——————————————————————————————————————————————————————————
DAY12 PM
try {
/*
* 使用DOM解析XML的大致步骤:
* 1:创建SAXReader
* 2:使用SAXReader读取要解析的XML文档并返回一个Document对象,
* 该步骤就是DOM解析耗时耗资源的地方,因为要先将XML文档内容全部读取完毕并载入内存
* 3:Document对象表示的就是该XML文档从Document中首先获取根元素
* 4:从根元素中按照XML的层级结构逐级获取子元素以达到遍历XML文档内容的目的
*/
/*
* 将emplist.xml文档中的所有员工信息解析出来,并转化为若干emp实例存入一个集合中
*/
//1.dom4j提供的SAXReader。不遵守java规则,没传入低级流。跟PrintWriter一样不需要自己套低级流了
SAXReader reader = new SAXReader();
//2.但是,是在read里面传,而不是跟java一样在new的时候.这一句Document把文档转成了对象
Document doc = reader.read(new FileInputStream("emplist.xml"));
+ /*step:3
*通过Document获取元素。Element也是dom4j提供的
*Element getRootElement()
*该方法用来获取XML文档中的根元素
*Element的每一个实例用于表示XML文档中的一个元素(一对标签)
*
*对于当前的例子来说,获取的即使emplist.xml文档中的<list>标签
*/
Element root = doc.getRootElement();
/*
* Element提供了获取子元素的相关方法:
* 常用的:
* List elements()
* 获取当前标签下的所有子标签,返回的集合中为若干Element实例,而每一个实例就表示其中的一个子标签
*
* List elements(String name)
* 获取当前标签下所有同名子标签
*
* Element element(String name)
* 获取当前标签下指定名字的子标签
*/
//emp是指定了标签,如果不指定就全部。当前没有其他标签,也可以不写从而获取全部
//获取当前根标签<List>下的所有子标签<EMP>
List<Element> eles = root.elements();
//用来存入每个员工信息的集合
List<Emp> list = new ArrayList<Emp>();
}catch(....){}
————————————————————————————————————————————————————————
//添加age
Element ageEle = empEle.addElement("age");
ageEle.addText(emp.getAge()+""); //最简单就是加空字符串
//salary 也可以用String.valueOf(int类型)转成字符串
empEle.addElement("salary").addText(String.valueOf(emp.getSalary())); 也可以用String.valueOf(int类型)转成字符串
xml的自动格式化:ctrl+shift+f
java也能用。但是在输出时也可以加参数以一定格式输出
//4 XMLWriter()还支持加参数,在输出的同时定义格式。OutputFormat.createPrettyPrint()
XMLWriter writer
= new XMLWriter(new FileOutputStream("myemp.xml"),OutputFormat.createPrettyPrint());
————————————————————————————————————————————————————————
XPath 以写路径的方式,快速地定位到指定的元素
.表示当前目录
..表示上级目录
<book></book>
//book忽略层级,不管在哪一级,是book就选取
@选取的是属性如,@lang
谓语[]
bookstore/book[1] 表示选择bookstore中的第一个book元素
book[last()]最后一个
bookstore/book/price[.price>35] book下的price自己大于35
bookstore/book/price[price>35] book下的price的price子项大于35
XPath不是必要组件,maven没一起下载下来
搜索jaxen,选择1.1.4
XPath在Document下,因此需要创建
SAXReader reader = new SAXReader();
Document document = reader.read(new FileInputStream("myemp.xml"));
String xpath = "/list/emp[age>23 and gender = '女'] /name"; //与,或,只能用and和or,小写
——————————————————————————————————————————————————————————
3部分功能,3天,1天一个部分
Test04.class.getClassLoader().getResourceAsStream()
由于在xml中是属性信息:
<emp id="2">
<name>王克晶</name>
<age>22</age>
<gender>女</gender>
<salary>4000</salary>
<hiredate>2008-02-15</hiredate>
</emp>
因此不能也跟其他标签一样用element,而应该用attribute,属于Attribute类型。但是要转成int只能通过将获取的属性类型的用getValue()方法转换,再用parseInt转成int类型,如下:
int id =Integer.parseInt(empEle.attribute("id").getValue());
——————————————————————————————————————————————————————
犯错:
//比较器要有类型<T>
Collections.sort(list,new Comparator<Emp>(){
public int compare(Emp e1,Emp e2) {
//不能直接获取变量salary,只能通过方法获取
return e1.getSalary()-e2.getSalary()>0?1:-1;
}
});
————————————————————————————————————————————————————————————
项目:
wtmpx是Linux的日志文件记录,是二进制文件。固定的大小372个字节
日志的分配:
user//...// pid进程id//type日志的类型,是整数7就是上线,8就是下线//time日志的生成时间//host用户的ip//...//...
各个客户端的日志中上线和下线的对应好数据,上传到服务器汇总
怎么解析?类型都不一样。user是字符串,pid是int,type:short time:int: host:String
raf.readInt
seek
项目主要就是解析日志,配对,发送给服务端
day1:从wtmpx解析日志保存到log.txt。把五部分读出来存到log.txt,按,分开
last-position.txt,内容为:3720
下次读的seek的开始位置
1:xml知识
2:io
3:解析
XMLread:
Element element(String name);
String elementText()
String elementTextTrim(String name);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
首先,全部在集合中都是String
如果是int类型,把对应的map的value赋值给它
如果是String类型,也是跟int一样赋值
如果是file类型,那就需要先在硬盘上写出文件,如果是txt,就加后缀txt,如果没有,就用raf写
将输入的line字符串调用getBytes()给转成字节流,因为raf的write只能写入int类型
raf.write(line.getBytes());
——————————————————————————————————————————————————————————
配对日志。用户名 pid一样 7 8 一样 就可以。
日志按行读 br 再转成实例,读到集合
下线的时间减去上线的时间
没必要在单一的集合寻找。
配对就是上线下线
先不马上配对,先创建2个map,
List<LogData>
loginMap logoutmap
lidz,441232 | logdata huangr 12348 | LogData
__________________________________
先想没有计算机自己如何解决。想清楚了再用代码实现
<logfile>wtmpx</logfile>
logFile就是
//第一步:解析日志所需属性
//unix系统日志文件
private File logFile = new File("wtmpx");
//保存解析后日志的文件
private File textLogFile = new File("log.txt");
//书签文件
private File lastPositionFile = new File("last-position.txt");
//每次解析日志的条目数
private int batch = 10;
logFile表示的文件也就是wtmpx文件,从中读取10条日志。
假设能够直接读取,先判断,如果textLogFile存在,则跳过,
if textLogFile 存在,说明读取过了,就不循环读取了。return true
否则,循环10次读取
IOUtil类中有
》》》》》》》
static List<LogData> loadLogData(File file) 方法,
* 从给定的文件中读取每一条配对日志,并存入
* 一个集合中然后返回。
》》》》》》》
saveLong方法,
传进long和File类型就给你把long转成字符串写到file文件中取,
通常用来记录指针位置
》》》》》》》
saveCollection(Collection c,File file)
该方法用来把给定的集合写道指定的file文件中
》》》》》》》
readString(RandomAccessFile raf,int length)
* 从给定的RandomAccessFile当前位置开始连续
* 读取length个字节,并转换为字符串后返回
》》》》》》》
readLong(File file)
* 从给定文件中读取第一行字符串,然后将其
* 转换为一个long值后返回
DMSClient
》》》》》》》
private long hasLogs(){
/**
* 第一步解析日志中的一个环节,
* 根据书签文件记录的位置判断是否还有
* 日志可以解析,若有,则将上次最后的位置
* 返回,若没有则返回-1。
* @return
*/
》》》》》》》
boolean parseLogs()
* 第一步:解析日志
* @return true:解析成功
* false:解析失败
————————————————————————————————————————————————————————
指定类型和线程池的初始化:
//用来接收客户端连接的服务端的ServerSocket
private ServerSocket server;
//用来管理处理客户端请求的线程的线程池
private ExecutorService threadPool;
//保存所有客户端发送过来配对日志的文件
private File serverLogFile;
初始化如下:
try {
serverLogFile = new File( config.get("logrecfile"));
//Executors是一个类,而Executor是一个接口,ExecutorService继承了该接口
//类名.说明newFixedThreadPool是静态方法,该方法需要传参,类型是int的类型的线程
threadPool = Executors.newFixedThreadPool(Integer.parseInt(config.get("threadsum"))); //30
server = new ServerSocket(Integer.parseInt(config.get("serverport")));//8080
}
keyset entryset返回的都是map
values()返回的是Colletion
values比list高级
————————————————————————————————————————————————————————
HashMap<K,V>
————————————————————————…^^^^^^^^
keySet
public Set<K> keySet()
此映射所包含的映射关系的 set 视图。
————————————————————————…^^^^^^^^
values
public Collection<V> values()
此映射中包含的值的 collection 视图
————————————————————————…^^^^^^^^
entrySet
public Set<Map.Entry<K,V>> entrySet()
此映射所包含的映射关系的 set 视图。
————————————————————————————————————————————————————————
双缓冲:倒水和喝水。不能同时倒水和喝水,因此建2个杯子,你倒水A的时候我喝水B,我喝水A的时候你倒水B,即双缓冲
BlockingQueue
ArrayBlockingQueue FIFO
LinkedBlockingQueue FIFO最常用
PriorityBlockingQueue:排序不是FIFO的,要求传入的是能够比较大小的。而是依据对象的自然排序顺序或者构造函数的Comparator决定顺序的
——————————————————————————————————————————————————————
这里的getValue方法不是其他类型转字符串,而是Attribute下面的方法,获取属性的值
eleEmp.attribute("id").getValue()
————————————————————————————————————————————————————————
apache下的commons下的jar包提供了java没有的方法,包括StringUtil。也有删除目录的方法
————————————————————————————————————————————————————————
Oracle:
自定义连接名
wh
123456
176.137.6.30/64
1521
xe
测试成功就连接
————————————————————————————————————————————————————————
聊天程序私聊思路:
首先,将在线的昵称都放进一个集合
如果 用户输入的以@开头
那么,遍历昵称集合,如果
匹配,则开启一个线程,进入私聊
不匹配,则提示昵称错误,然后跳出
否则 就把数据放入普通集合
——————————————————————————————————————————————
回顾:
String的方法:
/*
* String substring(int start,int end)
* 截取字符串,从start处开始到end处(不包含end处字符)的字符串截取出来并返回
* java api中有一个特点,通常使用两个数字表示范围时都是含头不含尾的
*/
/**
* String trim()
* 去除当前字符串两边的空白字符
*/
/**
* char charAt(int index)
* 获取当前字符串中指定位置的字符
*
/**
* boolean startsWith(String str)
* boolean endsWith(String str)
* 判断当前字符串是否是以给定字符串开始或结束的。
*/
/**
* String toUpperCase()
* String toLowerCase()
* 将当前字符串中的英文部分转换为全大写或小写
*
*/
VVVVVVVVVVVVV!!!!!!!!!!!!!!!!!!!!VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
/**
* String 提供了若干重载的静态方法valueOf
* 作用是可以将java其他类型转换为字符串,常用于将基本类型转换为字符串使用。
*
* 字符串是不变对象,每次修改都会创建一个新对象;
*/
int i=123;
String istr = String.valueOf(i);
System.out.println(istr+4);
————————————————————————————————————————————
/**
* 频繁修改字符串用StringBuilder
* java.lang.StringBuilder
* 由于String的设计不适合频繁修改内容,所以java提供了一个专门的用来修改字符串内容的类
* StringBuilder
* 其提供了编辑字符串内容的相关方法。性能很好。
*
*/
StringBuffer builder = new StringBuffer(str); //线程安全的,多线程常用,同步处理的,性能稍慢
StringBuilder builder = new StringBuilder(str);// jdk1.5后出现的.非线程安全,并发处理,性能稍快。
/*
* 追加字符串操作
* StringBuilder append(String str)
* 向当前字符串末尾追加给定字符串内容。
*/
builder.append(",为了找个好工作!");
将指定范围内的字符串替换为给定字符串
builder.replace(9,16,"就是为了改变世界!");
/*
* StringBuilder delete(int start,int end);
* 删除指定范围内的字符串
*/
//,就是为了改变世界!
builder.delete(0,8);
* StringBuilder insert(int index,String str)
* 向指定位置插入指定字符串
//反转字符串
builder.reverse();
————————————————————————————————————————
/**
* Wrapper
* 包装类的出现是为了解决基本数据类型不能直接参与面向对象开发的问题;
*
* 6个数字类型的包装类继承自Number
* Number是一个抽象类,提供了可以在6个基本类型数字间转换的相关方法。
*/
/*
* 基本类型=>包装类
*可以直接new,也可以使用包装类的静态方法valueOf(推荐)
*Integer的valueOf会重用1个字节之内的整数包装类对象。
*
*/
Integer i1 = Integer.valueOf(1);
Integer i2 = Integer.valueOf(1);
//还原成原来的值
int i = i1.intValue();
_______________________________——————————————————————————————————
/**
* 包装类有两个常量: MAX_VALUE, MIN_VALUE
* 分别表示包装类对应的基本类型的最大和最小值
*
*
*/
int imax = Integer.MAX_VALUE;
System.out.println(imax);
——————————————————————————————————————————
/**
* 包装类支持一个静态方法:parseXXX(String str)
* 可以将给定的字符串解析为对应的基本类型数据
* 前提是该字符串能正确描述基本类型可以保存的值
*
*/
String str1 = "123.123";
String str = "123";
//int i1=Integer.parseInt(str1); //编译报错,数值格式异常,int存不下123.123
int i=Integer.parseInt(str);
——————————————————————————————————————————————
package day02;
/**
* JDK1.5推出了一个新的特性:自动拆装箱
* 该特性是编译器认可,而不是虚拟机认可。
* 编译器在编译源程序时发现基本类型和引用类型需要转换时会自动补全转换代码
* 好处是程序员无需再关注基本类型与其对应的包装类之间的转换工作;
*/
public class IntegerDemo4 {
public static void main(String[] args) {
/**
* 触发自动拆箱,即:补全代码转为基本类型
* 下面的代码在编译后.class文件中的样子
* int i=Integer.valueOf(123).intValue();
*/
//jdk1.5开始类自动装箱。这里实际上已经变成:int i=Integer.valueOf(123).intValue();编译器自己补充了
int i=Integer.valueOf(123);
/**
* 触发自动装箱,即补全代码转换为包装类
* 下面的代码在编译后的.class文件中的样子:
* Integer in=Integer.valueOf(123);
*/
//jdk1.5开始类自动装箱。这里实际上已经变成:Integer in=Integer.valueOf(123);编译器自己补充了
Integer in =123;
}
}
——————————————————————————————————————————
/*
* Object的equals内部就是使用"=="比较的,所以若不重写没有实际价值。
*/
——————————————————————————————————————————
/*
* String支持正则表达式方法一:
* boolean matches(String regex)
* 判断当前字符串是否满足给定的正则表达式的格式
*
*/
/**
* String[] split(String regex)
* 将当前字符串中满足正则表达式的部分进行拆分,返回被拆分后的若干字符串。返回的数组中的每个元素为一段字符串。
*/
/**
* String replaceAll(String regex,String str)
* 将当前字符串中满足正则表达式的部分替换为给定的字符串
*
*/