——正则表达式
正则表达式
——Date类
Date类十一个较为常用的类,但是操作的日期格式会有一些不符合于个人的要求,而如果要想进一步取得一些自己需要的时间,则可
以使用Calendar类。
Date类本身使用非常简单,直接输出其实例化对象即可获取当前时间。
java.util.Date
getTime()
获取从1970年1月1日到现在个毫秒值。
java.sql.Date
构造方法
Date(long date)
long date:从1970年1月1日到指定日期的毫秒值参数,还可以使用new Date().getTime()作为参数
——Calendar类
使用此类可以将日期精确到毫秒。
Calendar是一个抽象类,无法直接使用,此时就要利用对象多态的概念,通过向上转型关系实例化本类对象。
通过Calendar类取得一个完整的日期,使用其子类GregorianCalendar来创建对象。
通过此类就可以非常轻松的取得一个完整的日期,但是在取得月份的时候要特别注意,需要月份+1,因为月份是从0开始计算。
如果按照此种方式取得,则也会很麻烦,最好的做法是将Date进行一些相关的格式化操作。
方法:
add(int Calendar.YEAR, int 偏移量)
偏移量可以为负值。
set(inte year,int month,int day)
需要注意的是month是从0开始的。
总结:
虽然Date类直接取出的时间格式并不是十分理想, 但是其作用依然很大。
通过Calendar类可以取得完整的时间。
——DateFormat类
位于java.text包中。
此类是一个日期的格式化类,专门格式化日期的操作,因为java.util.Date类本身就已经包含了完整的日期,所以只需要将此日期按照
一些好的格式格式化一下显示就好了。
虽然DateFormat是Format的子类,但是从定义上可以发现,此类是一个抽象类,按照以往的思路,直接使用其子类实例化即可。
但是DateFormat类本身的内部提供了可以直接为其实例化的操作。
public static final DateFormat getDateInstance()
得到日期的DateFormat对象。
public static final DateFormat getDateTimeInstance()
得到日期时间的DateFormat对象。
直接使用DateFormat类完成Date类的转换功能。
public class DateDemo01 {
——SimpleDateFormat类
以指定格式输出日期和时间。
此类的功能是完成日期的显示格式化的,例如在开发中可能会将一种日期格式转换为另外一种日期格式,如下所示:
原始日期: 2016-3-17 23:52:15
转换后如期:2016年3月17日23:52:25
以上的两个日期中的数字是完全一样的,唯一不同的是日期的显示格式不同,所以要想实现这样的转换功能就必须依
靠SimpleDateFormat类。
如果想要实现转换,则必须首先准备好一个模板,通过此模板进行日期数字的提取工作。
格式如下:
y:
表示年,因为年份是4位数字,所以使用“yyyy”来表示。
M:
表示月,因为月份是2位数字,所以使用“MM”来表示。
d:
表示日,因为日是2位数字,所以用“dd”来表示。
H:
表示时,因为时是2位数字,所以用“HH”来表示。
m:
表示分,因为分是2位数字,所以用“mm”来表示。
s:
表示秒,因为秒是2位数字,所以用“mm”来表示。
S:
表示毫秒,毫秒是3位数字,所以用“SSS”来表示。
在SimpleDateFormat类使用的时候,必须注意的是在构造对象时要传入匹配的模板。
构造方法:
SimpleDateFormat(String pattern)
用给定的模式和默认语言环境的日期格式符号构造
SimpleDateFormat
。实例化模板对象,使用模板对象来
转换:
public Date parse(String prase, ParsePosition pos)
将指定字符串转换为Date对象。
此时取得的是全部的时间数。
格式化:
public final String format(Date date)
将旧的日期重新格式化。
将时间重新格式化成字符串显示。
System.out.println(sdf1.format(d));
------------------------------------------------------------------------------------
输出结果:
总结:
1、DateFormat可以直接使用,但其本身十一个抽象类。
2、SimpleDateFormat类是DateFormat类的子类,一般情况下来将DateFormat类很少会直接使用,而是使用SimpleDateFormat。
——取得当前日期
开发中经常需要取得日期,而且每次取得日期的时候代码都会重复,所以既然是重复的代码就可以将其定义成一个类,以方便重复
调用,但是在操作的时候有一点特别需要注意:
如果月份是9月,则应该显示09,但是如果是09的话,则肯定会忽略掉数字0。
1、先通过Calendar操作
public class CalendarDemo01 {
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
——String类
String类是final类,不能有子类对象,也不能复写String类的方法。
1、字符串是一个特殊的对象。
String st = new String(); // String st = "abc";
2、字符串一旦初始化就不可以被改变。
String str1 = "abc";
str1 = “def”;
System.out.println(str1);
输出结果为:def 但是字符串“abc”本身没有变化,变化的是str1的地址指向。对象没变,变化的是引用变量。
3、字符串的定义
String str1 = "abc"; //str1是一个类类型变量,“abc”是一个对象。
String str2 = new String("abc");
有什么区别?
str1在内存中有一个对象,str2在内存中有两个对象,new String()和“abc”
在内存中有一个常量池,常量池是一个数组
使用起来是一样的,但是
System.out.println(str1 == str2); 结果为false,因为str1和str2是两个对象。
System.out.println(str1.equals(str2)); 结果为true,因为String类复写了Object类中的equals方法,功能是将此字符串与指定的
对象比较,判断字符串是否相同。
4、字符串的获取和判断
String s1 = "abc";
String s2 = new String("abc");
String s3 = "abc";
System.out.ptintln(s1 == s2);
System.out.println(s1 == s3);
s1 == s2 运行结果为false;
s1 == s3 运行结果为true,因为作为一个已经在常量池中存在的字符串,不会再开辟新的内存空间,因为再开辟空间的话会浪费空间
获取:
(1)字符串中包含的字符数,也就是字符串的长度。
int length(); 获取长度。
数组也有length,但是这是数组的属性,数组的length不带括号,而字符串的length是方法。
(2)根据位置获取位置上的某个字符
char charAt(int index);
(3)根据字符获取该字符在字符串中的位置
int indexOf(int ch);//函数重载
参数为ASCII码,如果有两个相同的字符,返回的是ch在字符串中第一次出现的位置。如果没有找着,返回-1。
int indexOf(int ch,int fronIndex);从fromIndex指定位置开始,获取ch在字符串中出现的位置。
(4)根据字符串获取小串在大串中的位置。
int indexOf(String str);
int indexOf(String str,int fromIndex);
输出脚标还是从0开始计算,如果定位大于字符串所在位置,则返回-1,如果字符串不存在,则返回-1;
(5)反向索引,从右向左开始查找,但是下标不会变,还是1、2、3、4、5、6
int lastindexOf(int ch);
int lastindexOf(int ch,int fromIndex);
int lastindexOf(String str);
int lastindexOf(String str,int fromIndex);
如果查找的字符或字符串不存在,则返回-1。
(6)获取字符串中的一段字符串。
String substring(int begin);
从指定位置开始到结尾,如果脚标不存在,会出现脚标越界异常。
String substring(int begin,int end);
包含头,但不包含尾。
该方法也可以从指定位置开始获取到结尾的字符串:
String substring(int begin,s.length());
判断:
(1)字符串中是否包含某一个子串。
boolean contains(str);
特殊之处:
indexOf(str)可以索引str第一次出现的位置,如果返回-1,表示str不存在于该字符串,所以,也可以用于判断是否包含。
if(str,indexOf("aa") != -1) 该方法既可以判断,又可以获取出现的位置。
(2)字符串中是否有内容。
boolean isEmpty();
当且仅当length()为0时返回true。“”和null不同,“”是个对象,运行结果为true,null是指向空。
(3)字符串是否以指定内容开头。
boolean startsWith(str);
(4)字符串是否以指定内容结尾。
boolean endsWith(str);
(5)判断字符串内容是否相同,复写了Object类中的equals方法。
boolean equals(str);
(6)判断内容是否相同,并忽略大小写。
boolean equalsIgnoreCase();
转换:
(1)将字符数组转成字符串
构造函数:
String(char[ ])
String(char[ ],offset,count)
例:
char [ ] arr ={a,b,c,d,e,f,g};
String st = new String(arr,1,3);
System.out.println(st);
输出结果为:bcd arr是数组,1是起始位,3是个数。
静态方法:
static String copyValueOf(char[ ]);
static String copyValueOf(char[ ] data,int offset,int count);
(2)将字符串转成字符数组
char[ ] toCharArray() 该方法没有参数,因为是字符串调用,字符串就是对象。
(3)将字节数组转成字符串
String(byte[ ])
String(byte[ ],offset,count) 将字节数组中的一部分转成字符串。
(4)将字符串转成字节数组
byte[ ] getBytes()
字符串和字节数组在转换过程中,是可以指定编码表的。
(5)将基本数据类型转成字符串
static String valueOf(int i);
返回int参数的字符串表示形式。
8+" "和valueOf(8)是一样的
static String valueOf(double d);
返回double参数的字符串表示形式。
static String valueOf(float f);
返回float参数的字符串表示形式。
static String valueOf(char c);
static String valueOf(boolean b);
返回boolean参数的字符串表示形式。
static String valueOf(Object obj);
返回Object参数的字符串表示形式。
static String valueOf(long l);
替换:
(1)新字符替换旧字符,然后返回一个新字符串。
String replace(char oldChar,char new Char)
如果要替换的字符不存在,返回的还是原串。
(2)新字符串替换旧字符串,然后返回一个新字符串
String replace(CharSequence target,CharSequence replacement)
切割:
(1)将参数作为分隔符分开,以字符串数组形式返回。
String[ ] split(regex)
例:
String s = "zhangsan,lisi,wangwu";
String [ ] arr = s.split(",");
for(int i = 0; i < arr.length; i++)
System.out.println(arr[i]);
则输出结果为:
zhangsan
lisi
wangwu
大小写转换
(1)将字符串转换成大写
String toUpperCase()
(2)将字符串转换成小写
String toLowerCase()
去除空格
将字符串两端的空格去除
String trim();
比较
对两个字符串进行自然顺序的比较
int compareTo(String st)
如果参数字符串等于此字符串,则返回值为0;如果此字符串小于参数字符串,则返回负数;如果此字符串大于参数字符串
,则返回正数。
——StringBuffer
1、字符串的组成原理就是通过该类实现的。
2、StringBuffer可以对字符串内容进行增删。
3、StringBuffer是一个容器。
4、StringBuffer很多方法和String相同。
5、StringBuffer长度可变化。
StringBuffer是字符串缓冲区,是一个容器。
特点:
具备增删改查(CURD)。
长度是可变化的。
可以直接操作多个数据类型。
可以通过toString方法变成字符串。
当数据类型不确定而且长度也不确定,最终变成字符串的时候可以使用StringBuffer。
(1)存储
追加 append
StringBuffer append();
将指定数据作为参数添加到已有数据的结尾处。
StringBuffer sb = new StringBuffer();
sb.append("abc").append(true).append(123);
方法调用链:返回的还是本类对象,所以还可以调用本类方法。
插入 insert
StringBuffer insert(index,数据);
将数据插入到指定index位置。
(2)删除
StringBuffer delete(start,end);删除缓冲区中的数据,包含头不包含尾。
StringBuffer deleteCharAt(index);删除指定位置的字符。
(3)获取
char charAt(int index);
通过位置获取字符。
int indexOf(String str);
通过字符串获取位置。
int lastIndexOf(String str);
通过字符串反向索引。
int length();
String subString(int start,int end);
注意,该方法返回的还是一个String,并不是StringBuffer。
(4)修改
StringBuffer replace(int start,int end,String str);
将指定位置的字符串替换成参数str,包含头不包含尾。
void setCharAt(int index,char ch);
注意,该方法无返回值,只替换,不返回。
(5)反转
StringBuffer reverse();
(6)将缓冲区中的指定数据存储到指定字符数组中。
void getChars(int srcBegin,int srcEnd,char[ ] dst,int dstBegin);
—从srcBegin处开始复制。
—在srcEnd处结束复制。
—dst用来保存复制数据的数组。
—从dst数组的dstBegin处开始保存。
(7)JDK1.5版本之后出现了StringBuilder
StringBuffer是线程同步。
StringBuilder是线程不同步。
单线程用StringBuilder。
多线程用StringBuffer,多线程也可以用StringBuilder。自己加锁。
建议使用StringBuilder。
方法不变,功能相同,区别在于锁,StringBuilder无锁,需要自己加锁。
锁不重要,效率最重要。
JDK升级的三个因素:
1、提高效率
2、简化书写
3、提高安全性
——基本数据类型对象包装类
byte Byte
short Short
int Integer
long Long
boolean Boolean
float Float
double Double
char Character
基本数据类型对象包装类的最常见作用就是用于基本数据类型和字符串类型之间做转换。
(1)基本数据类型转成字符串:
基本数据类型+“ ”
基本数据类型.toString(基本数据类型值);
例:Integer.toString(123);
(2)字符串转成基本数据类型
静态:
xxx a = Xxx.parseXxx(String);
int a = Integer.parseDouble("123");
将字符串参数作为有符号的十进制正数进行解析。
double b = Double.parseDouble("11.22");
boolean bool = Boolean.parseBoolean("true");
非静态:
Integer i = new Integer("123");
构造一个新分配的Integer对象,它表示String参数所指示的int值。
int num = i.intValue();
(3)十进制转成其他进制
toBinaryString();
toHexString();
toOctalString();
(4)其他进制转成十进制
parseInt(String,radix);
JDK1.5新特性
public class Demo3
——Collection集合类(集合框架)
Collection集合方法:
增:boolean add(E e)
删:boolean remove(Object o) void clear()
查:boolean retainAll(Collection c) int size()
判断:boolean contains(Object o) boolean isEmpty()
1、为什么出现集合类
面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的
一种方式。
2、数组和集合类同是容器,有何不同?
数组虽然也可以存储对象,但长度是固定的;集合长度是可变的。数组中可以存储基本数据类型(同一种数据类型),集合只能存
储对象(多种类型)。
3、集合类的特点
集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。
注意:集合和数组一样,存放的都是对象的引用(地址)。
4、Collection类的方法
boolean add(E e)
add方法的参数类型是Object,以便接受任意类型的对象。
add方法是把对象的地址值添加到集合对象中。
boolean addAll(Collection c)
添加一组元素。
void clear()
清空容器。
boolean remove(Object o)
删除一个元素。
boolean removeAll(Collection c)
删除一组元素,非并全部。
boolean contains(Object o)
判断是否包含指定元素。
boolean containsAll(Object o)
判断是否包含指定的一组元素。
对象存放顺序不同也为真。
boolean equals(Object o)
比较对象。对象存放顺序不同为假。
int hashCode()
返回此collection的哈希值。
boolean isEmpty()
判断是否为空。
如果返回值为0,则isEmpty为true。
int size()
返回此collection的元素个数。
boolean retainAll(Collection c)
取交集。
Object[ ] toArray()
返回包含此collection中所有元素的数组。
Iterator iterator()
返回在此collection的元素上进行迭代的迭代器。
——迭代器 iterator
迭代器其实就是集合取出元素的方式。
Iterator是一个接口。
1、迭代器成员方法
boolean hasNext()
如果仍有元素可以迭代,则返回true。
E next()
返回迭代的下一个元素。
void remove()
从迭代器指向的collection中移除迭代器返回的最后一个元素。
——内部类实现接口的方式
把取出方式定义在集合的内部,这样取出方式就可以直接访问集合内部的元素,那么取出方式就被定义成了内部类。而每一个容器的数据结构也不同,所以取出动作细节也不一样,但是都有共性内容:判断和取出,那么可以将这些共性抽取,形成一个接口。那么这些内部类都符合一个规则,该规则是Iterator。
如何获取集合取出方法的对象呢?
通过一个对外提供的方法:iterator();
——List集合共性方法
List:
凡是可以操作脚标的方法都是该体系特有的方法。
增:
void add(int index,E element);
boolean addAll(int index,Collection c);
删:
E remove(int index);
remove方法会返回被移除的元素。
改:
E set(int index,E element);
查:
E get(int index);
List subList(int fromIndex,int toIndex);
返回子列表,包含头不包含尾。
int indexOf(Object o);
通过对象获取对象位置。
Collection
|--List:元素是有序的,元素可以重复,因为该集合体系有索引。
|--ArrayList:底层的数据结构使用的是数组结构。特点:查询快,增删慢,相对于Vector,线程不同步,增量增加50%,允许null
|--LinkedList:底层使用的是链表数据结构。特点:查询慢,增删快,允许null。
|--Vector:底层是数组数据结构,因为线程同步,效率低,被ArrayList替代了,增量翻倍。
|--Stack:继承了Vector,实现了堆栈操作
|--Set:元素是无序的,元素不可以重复,因为该集合无索引。
无序:存入和取出的顺序不一致。
|--HashSet:底层数据结构是哈希表。
|--TreeSet:可以对Set集合中的元素进行排序。(自然排序,按照ASCII码表进行排序)
——List集合特有的迭代器,ListIterator是Iterator的子接口。
在迭代时,不可以通过集合对象的方法操作集合中的元素,否则会产生并发异常ConcurrentModificationException,所以在迭代时,
只能用迭代器的方法操作元素,可是Iterator的方法是有限的,只有hasNext、next和remove方法,只能对元素进行判断,取出,删
除的操作。如果想要进行其他的操作,比如添加修改等,就需要使用其子接口ListIterator。该接口只能通过List集合的listIterator方法
获取。
ListIterator方法:
void add(E e)
插入元素。
void remove()
删除元素。
void set(E e)
用指定元素替换next或者previous返回的最后一个元素。
E previous();
逆向遍历。
boolean hasPrevious();
li.set("007");
——LinkedList方法
void addFirst(E e)
将指定元素插入此列表的开头。
void addLast(E e)
将指定元素添加到此列表的尾部。
在JDK1.6版本之后,出现了offerFirst和offerLast,与addFirst和addLast功能相同,区别在于:offerFirst和offerLast返回值为boolean
E getFirst()
返回此列表的第一个元素。
E getLast()
返回此列表的最后一个元素。
如果此列表为空,则抛出NoSuchElementException异常。
在JDK1.6版本之后,出现了peekFirst和peekLast,与getFirst和getLast功能相同,区别在于:如果此列表为空,不抛异常,返回null。
E removeFirst()
返回并删除第一个元素。
E removeLast()
返回并删除最后一个元素。
如果此列表为空,则抛出NoSuchElementException异常。
在JDK1.6版本之后,出现pollFirst和pollLast,与removeFirst和removeLast功能相同,区别在于:如果此列表为空,不抛异常,返
回null。
——Vector方法
枚举是Vector类的特有迭代器。
Enumeration elements()
Enumeration en = v.elements();
boolean hasMoreElements()
判断是否还有元素。
E nextElement()
返回下一个元素。
Enumeration底层数据结构是哈希表,元素是无序的。
枚举和迭代器是一样的,因为枚举方法名过长,所以被迭代器替换了。
Vector增量翻倍。
枚举只有Vector有。
void addElement(E obj)
将指定的组件添加到此向量的末尾,将其大小增加1。
int capacity()
返回此向量当前容量。
boolean contains(Object o)
如果此向量包含指定的元素,则返回true。
E elementAt(int index)
根据位置获取元素。
Enumeration elements()
返回此向量的组件的枚举。
..........略
——Stack
Stack继承了Vector类,实现了堆栈操作。
Stack类方法
boolean empty()
判断堆栈是否为空。
E peek()
获取栈顶对象,但不移除栈顶对象。
E pop()
移除栈顶对象并返回被移除的对象。
E push(E e)
将元素压入栈顶。
int search(Object o)
根据对象返回对象在堆栈中的位置,以1为基数(数组中以0位基数)
——用LinkedList模拟出队出栈
——ArrayList去除重复的元素
}
——ArrayList去除重复对象
——Set集合
Set集合共性方法与Collection集合共性方法一致。
——HashSet
无序
不可重复
Set:元素是无序的(存入和取出的顺序不一定一致),元素不可以重复,因为该集合无索引。
关于哈希值和地址:
哈希值和地址值是不一样的,哈希值是通过哈希算法散列得来的,而地址值是通过是和物理层面有关,是系统分配的,是不存在相
同的,而哈希值是可以通过强制手段设置为相同的,也就是说哈希值是一种逻辑上的确保唯一性,而地址值就是物理上确保唯一性。
package set;
import java.util.Iterator;
*
* 2016年2月29日19:48:35
*
* 去除HashSet中的重复元素
*
*
* */
public class HashSetDemo {
public static void main(String[] args)
{
HashSet hs = new HashSet();
hs.add(new Person("lisi01",11));
hs.add(new Person("lisi02",12));
hs.add(new Person("lisi03",13));
hs.add(new Person("lisi03",13));
hs.add(new Person("lisi04",14));
hs.add(new Person("lisi04",14));
hs.add(new Person("lisi05",15));
hs.add(new Person("lisi06",16));
hs.add(new Person("lisi06",16));
hs.add(new Person("lisi06",16));
for(Iterator it = hs.iterator();it.hasNext();)
{
Person p = (Person)it.next();
System.out.println(p.getName() + " " + p.getAge());
}
}
}
{
private String name;
private int age;
Person(String name,int age)
{
this.name = name;
this.age = age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
public boolean equals(Object o)
{
if(!(o instanceof Person))
return false;
Person p = (Person)o;
return p.getAge() == this.getAge() && p.getName().equals(this.getName());
}
public int hashCode()
{
return name.length() + age*7;
}
}
对实现它的每个类的对象具备比较性。
TreeSet中存入的对象必须强制实现Comparable接口并复写compareTo()方法。
int compareTo(T o)
注意:
compareTo()方法中只能传对象,如果是基本数据类型,需要装箱。
new Integer(12);
将整型数据封装成整型对象。
TreeSet默认是升序排序,如果想改为降序排序,可以将compareTo方法的返回值改为1-> -1,-1->1。
所以如果想在TreeSet中存储对象,该对象所在的类必须先实现Comparable接口,因为往TreeSet中存储对象时,JVM会自动调用
compareTo方法来进行判断排序,如果排序时当主要条件相同,可以判断次要条件(需要通过复写来自定义compareTo方法)。
例如:
if(this.age == s.age)
{
return this.name.compareTo(s.name); //当年龄相同时再判断姓名,因为String类已经实现Comparable接口,所以可以直
接使用compareTo方法。
}
TreeSet集合只看compareTo方法返回的结果是整数还是负数还是零。如果返回值固定位1,则存储顺序是什么取出顺序就是什么,
如果compareTo返回的是1,则表示存入的数比第一个数大,所以存储顺序是固定的。
TreeSet保证数据唯一性的依据:
compareTo方法的返回值,TreeSet集合存储顺序与哈希值无关,只有HashSet才依据哈希值来判断元素。
TreeSet是通过compareTo方法来判断元素是否相同,只要不返回0,就存入元素,返回值为1升序存储,返回值为-1降序存储。
要想使用TreeSet删除或者判断元素是否相同,都是依据compareTo方法。
TreeSet排序的第一种方法:
让元素自身具备比较性,继承comparable借口,复写compareTo方法,这种方式也被称为自然顺序,或者默认顺序,因为继
承Comparable接口之后元素自身已经具备比较行,并且是按照字典顺序排序。
TreeSet的第二种比较方式:
当元素自身不具备比较性时,或者具备的比较性不是所需要的,可以定义一个比较器,在集合初始化时,将比较器Comparator传
递给TreeSet使其具备比较性。
TreeSet集合的构造方法:
TreeSet()
构造一个空的集合,根据元素的自然顺序进行排序
TreeSet(Comparator c)
构造一个空的集合,它根据指定比较器进行排序。
如果同时存在复写compareTo和传入比较器,则以比较器为准。
定义比较器:
定义一个类,实现Comparator接口,并复写compare方法。
Comparable中是compareTo方法,Comparator中是compare()方法,这两个方法都是以返回值来判断元素是否相同。
TreeSet底层数据结构:
因为每次存入对象,都要与所有对象都比较一遍,效率低下,为了优化效率,TreeSet底层使用二叉树来存储,比第一个元素小的
往左放,比第一个元素大的往右放,然后跟第二层比较,小的往左放大的往右放……当数据多了之后依然效率低下,为了解决该
现象,二叉树结点多了之后会自动折中,取一个中间值继续进行比较。
二叉树元素的取出方式:
由小到大的取出方式:
因为小值全都存放在左边,所以先取左边,(中序遍历)
TreeSet练习:
见到带泛型的类,用的时候往里传参数就可以了。
ArrayList al = new ArrayList();
al.add(“123123”);
al.add(“123123”)
al.add(123213);
Strings = (String)it.next();
如果将int 123213在取出过程中强制转换成Strign类型,则运行时会报错,而编译时不会报错,为了在解决这个安全问题 ,JDK1.5版
本以后出现的新特性,用于解决安全问题,是一个安全机制。
如果数据类型不一致,可以将错误从运行时期转移到编译时期,提高安全性。
如果不将迭代器Iterator加入泛型,则取出元素时(it.next())依然需要强制转换。
当使用JDK提供的数据类型时,Iterator不必使用泛型,当迭代器取出的是自定义对象时,为了避免强转强转的麻烦,可以加泛型
注: 有关详细信息, 请使用 -Xlint:unchecked 重新编译。”
——泛型类
就是带泛型的类。
ArrayList<E> E代表Elements元素,T代表type类型
|-- add(E e)
当ArrayList接收的泛型为String时,则add方法中接收的参数也是String(add(String s))
ArrayList<String>集合中只能传入String类型的数据,如果传入其它类型数据会编译错误。
——比较器Comparator中的泛型:
Comparator<T>
|-- compare(T1 t,T2 t2)
class MyComparator implements Comparator<String>
{
public int compare(String s1, String s2)
{
//在Comparator中加入泛型,可以避免将Object强转成所需类型,参数传入时就已经是指定类型了
//String s1 = (String)o1;
//String s2 = (String)o2;
}
}
在JDK1.5版本之前,通常使用Object来接收不同类型数据。
public class GenericDemo02 {
在JDK1.5版本出现泛型之后,增强了安全性,强制传入数据类型必须一致。
注意:
equals方法不能使用泛型,只能使用Object o。
u.setObject(new Worker()); //只能传入Worker类对象。
如果要定义静态泛型,则泛型一定定义在方法上。
public class GenericDemo04 {
当类定义泛型之后,如果方法泛型与类泛型一致,则方法只能操作对应泛型。
如果方法上单独定义泛型,则该泛型与类泛型无关。
类泛型可以与方法泛型同名,以方法泛型为准。
// public <T> void show(T t) //如果在方法上也定义泛型,则以方法泛型为准。
只有在 Demo<String> d = new Demo<String>();才会传入泛型参数,而静态在类加载时就已经存在了,所以静态无法访问非静态。
定义泛型不能乱写,泛型需要写在返回值前面。
public static <T> void method(T t)
——泛型接口
当实现接口时无法确定要操作的数据类型,所以将接口定义为泛型。
在实现接口的同时,在类上也加入泛型,使用方法同泛型类。
public class GenericDemo05 {
//所以无法操作String类型数据。
public class GenericDemo06 {
——泛型限定占位符:
// public static void print(ArrayList<?> al){
Iterator<?> it = it.iterator();
}
T t = it.next();
例如传入的是String
T t = it.next();
t.length();
无法使用
it.next().length();
如果要使用子类对象的特有方法,必须传入泛型,否则默认Object。
因为父类无法使用子类对象的特有方法。
如果只想接收Person类型或者Person的子类,可以使用泛型限定。
自动装箱就是Java编译器在编译期间自动将基本类型包装成数据对象。
integer num = new Integer(100);
自动装箱后就变成了:
Integer num = 100;
将100自动转换为数据包装对象。
自动拆箱:
将数据类型包装类转换成基本数据类型:
int num = new Integer(100);
public HashMap()
加载因子:达到加载因子就进行扩容。
此类实现一个哈希表,该哈希值将键映射到相应的值,任何非null的对象都可以用作键或值。
为了成功在哈希表中存储和获取对象,用作键的对象必须实现hashCode和equals方法。
向Map集合中添加元素。
void clear();
清空集合。
判断集合是否为空。
此时coll就可以获取迭代器来取出元素了。
Map<String,Integer> map = new HashMap<String,Integer>();
其实就是将键值对以对为单位存放到Set集合中,通过Map.Entry方法来获取Key和Value。
返回Map集合中的所有的键。
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("01",11);
map.put("02",12);
map.put("03",13);
map.put("04",14);
map.put("05",15);
//先使用keySet()方法获取Map集合中所有键的Set集合。
//使用Set集合来接收所有的键,因为Set集合具备迭代器。
//Set集合也具备泛型,所以最好使用泛型来接收keySet返回的键。
Set<String> keyS = map.keySet();
//迭代器Iterator的泛型和Set集合的泛型一致。
for(Iterator<String> it = keyS.iterator(); it.hasNext();)
{
String key = it.next();
String value = map.get(key);
//有了键key,就可以通过Map集合的get方法获取其对应的值。
System.out.println("key = " + key + ",value = " + value);
}
当Map集合调用entrySet的时候,返回的也是一个Set集合,也就是将Map集合中的映射关系取出。
一个键对应一个值就是一个映射关系,这个映射关系就是Map.Entry类型,通过迭代器返回的值就是一个映射关系。
一组映射关系就代表Map.Entry的实例对象,也就是it.next()取出的元素(对象),因为这个对象中既有key又有value,所
以Map.Entry<K,V>中提供了getKey()和getValue()方法。
for(Iterator<Map.Entry<String, Integer>> it1 = entryset.iterator();it1.hasNext();)
key和value可以看做是丈夫和妻子,entrySet取出的既不是丈夫(key)也不是妻子(value),取出的是丈夫(key)和妻
子(value)的关系,所以取出得到的值既不是丈夫(key)也不是妻子(value),而是Map.Entry。
因为取到了key与value的关系,所以可以通过Map.Entry中的getKey()和getValue()方法获取关系中的键和值。
——Map.Entry
其实Entry也是一个接口,它是Map接口中的一个内部接口,Entry是Map集合的嵌套类。
为什么Entry要定义成Map集合的内部接口?
因为Entry表示的是Map集合的映射关系,所以必须先有Map集合才能有Entry。
而且当Entry定义成内部类时,可以直接访问Map集合的内部元素。
Interface Map<T>
{
//能加static的接口一定是内部接口
public static interface entry
{
public abstract <T> K getKey();
public abstract <T> V getValue();
}
}
class HashMap implements Map
{
class HaHa<K,V> implements Map.Entry<K,V>
{
public K getKey(){}
public V getValue(){}
}
}
binarySearch专门用于对List集合进行查找,因为List集合具备脚标,并且但凡能够使用二分查找的,必须是有序集合。
用法与数组的二分查找相同。
如果查找的规则并不是所需的规则,可以自定义比较器。
ArrayList<String> al = new ArrayList<String>();
——Collections 替换和反转
替换:
replaceAll(List,oldVal,newVal)
将新值替换旧值。
ArrayList<String> al = new ArrayList<String>();
反转:
reverse(List)
将List集合元素反转。
Comparator reverseOrder()
该方法返回一个比较器,将集合的compare强行逆序。
(将List逆序排序)
可以将该方法直接放到TreeSet中,也可以在该方法中传入自定义比较器,将自定义比较器顺序强行逆序。
——Collections synchronizedList
传入一个List集合,返回一个同步的List集合。
将List传入synchronizedList之后,SynchronizedList类会对其进行加锁,从而达到同步的目的。
也可以对Set、Map进行加锁操作。
——Collections swap
reverse方法底层就是swap方法。
static void swao(List,int i,int j)
交换 i 脚标和 j 脚标的元素。
——Collections shuffle
public static voi shuffle(List list)
对列表顺序进行随机置换。
——Arrays
用于操作数组的工具类,里面都是静态方法。
描述系统的一些信息。
System类方法都是静态的:
out:标准输出,默认控制台。
in: 标准输入,默认键盘。
方法:
static long currentTimeMillis()
返回以毫秒为单位的当前时间。
void gc()
垃圾回收器。
String setProperties(String key,String value)
设置系统属性信息。
Properties getProperties();
获取系统属性信息。
获取的是虚拟机在启动时加载的一些默认的系统属性信息。
在JVM启动时动态加载一些系统属性信息:
java -D name=value lei
跨平台性:
System.get("os.name");
该方法可以跨平台,根据操作系统不同输出结果也不同。
根据此方法可以判断不同平台的不同操作。
——Runtime
每个Java应用程序都有一个Runtime类的实例对象,使应用能够与其运行的环境相连接,可以通过getRuntime()方法获取当前运行
时对象。
Runtime类无构造函数,不能直接创建对象,但是却有非静态方法,说明该类肯定提供了一个方法获取本类对象,而且该方法是静
态的,并且返回值类型是本类类型。
返回与当前Java应用程序相关的运行时对象,这个对象随着JVM的启动而产生。
该方法使用了单例设计模式,保证了对象的唯一性。
Process exec(String command)
根据传入的字符串命令执行,相当于在DOS中执行命令。
(可以使用循环写出小病毒)
Runtime r = Runtime.getRuntime();
r.exec("notepad.exe ***.txt");
该语句可以将txt文件复制到一个新的文本文档中,并且只要与其运行程序相关联的文件都可以打开,比如播放器和电影文件
Process类中又一个void destory()方法,可以杀掉子进程,但是destory方法只能结束被Runtime对象启动的方法,因为对于系统
进程无法获取其启动对象,所以无法结束。
Runtime r = Runtime.getRuntime();
Process p = r.exec("D:\360.exe");
Thread.sleep(4000); //使当前线程等待四秒
p.destory(); //杀掉子进程
——反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的
任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
应用在一些通用性比较高的代码中。
后面学到的框架,大多数都是使用反射来实现的。
在框架开发中,都是基于配置文件来开发的。
在配置文件中配置了类的相关路径,它(框架)就可以使用反射机制来得到类中的所有内容,也可以执行类中的某个方法。
类中的所有内容:属性,无参构造,有参构造,普通方法。
分析反射的原理:
第一步:创建Java文件
第二步:编译Java文件,生成class文件。
第三步:通过虚拟机加载类(class)文件,会在内存中生成一个Class类,来表示.class字节码文件。
如果得到了Class类,就可以得到这个类中的所有内容,包括:属性,构造方法,普通方法。
所以:如果想要使用反射机制,首先需要得到Class类。
得到Class类的三种方式:
类名.class
对象.getClass()
使用Class.forName(:路径“)
属性的表示类:Field
构造方法表示类:Constructor
普通方法表示类:Method
那么得到Class类之后,怎么对属性,构造方法和普通方法进行操作呢。
1、首先获取Class类。
Class class1 = Person.class;
Class class2 = new Person().getClass();
Class class3 = Class.forName("包名.类名");
2、面试题
对一个类进行实例化,可以new,那么如果不使用new,怎么获取对象?
答:可以使用反射机制来获取对象。
//首先获取Class 类的实例
Class c = Class.forName("完整类路径");
//得到某个类的实例
Person p = (Person) c.newInstance();
1)操作无参构造的代码
//得到Class
Class c - Class.forName("完整类路径");
//得到Person类的实例
Person p = (Person)c.newInstance();
//操作对象
p.setName("张三");
//输出值
System.out.println(p.getName());
2)操作有参构造方法的代码
import java.lang.reflect.Constructor;
//得到Class
Class c = Class.forName("完整类名");
//使用有参数的构造方法
//c.getConstructors();//获取所有的构造方法
//getConstructor()方法传递的参数是有参构造方法里面的参数类型,类型使用class形式进行传递,例如String.class,int.class
Constructor cs = c.getConstructor(String.class,int,class);
//通过有参数的构造方法设置值。
//通过有参数的构造方法来创建Person实例,并设置初始值
Person p = (Person)cs.newInstance("张三",20);
//输出
System.out.println(p.getName() + " " + p.getAge());
3)使用反射操作属性
import java.lang.reflect.Field;
//得到Class类实例
Class c = Class.forName("完整类名");
//得到Person类的实例
Person p = (Person)c.newInstance();
//c,getDeclaredFields(); //表示得到所有的属性
//得到name属性
Field f = c.getDeclaredField("name");
//操作的是私有的属性,默认不允许操作,需要通过setAccessible方法来设置可以操作私有属性。
f.setAccessible(true);
//设置name值,使用set方法传入两个参数,第一个参数是类的实例,第二个参数是需要设置的属性。
f.set(p,"李四"); //相当于p.name = "李四";
//获取name的值
System.out.println(f.get(p)); //相当于p.name
4)使用反射操作成员方法
使用Method类表示普通方法
//得到Class类的实例
Class c = Class.forName("完整类名");
//得到Person类实例
Person p = (Person)c.newInstance();
//得到普通方法
//c.getDeclareMethods(); //得到所有的普通方法
//getDeclareMethod()方法需要传递两个参数,第一个参数是方法名,第二个参数是参数类型。
Method = c.getDeclareMethod("setName",String.class);
//执行setName方法:传递两个参数,第一个参数是Person类的实例,第二个参数为该方法的参数值。
//执行了invoke方法后,相当于执行了setName方法,同时传入的参数为“王五”
m.invoke(p,"王五");
//输出
System.out.println(p.getName());
如果操作的是私有方法,需要:
m.setAccessible(true);
当操作的方法是静态方法时,不需要使用反射,因为静态方法可以直接使用类名调用,不需要类的实例。
使用反射操作静态的时候,也不需要实例,在invoke方法传参的时候传入一个null即可。
m.invoke(null,"王五");
反射应用:
在Web项目中的web.xml文件中,可以根据servlet-class的Servlet路径来找到对应的servlet实例,从而操作方法或
者属性,这也就是框架的底层机制。