日期类、泛型技术、IO技术
-
日期时间API
-
Date类介绍
-
Date类它是封装的时间对象。但是由于这个类不利于国际化,所以Date类中的大部分方法和构造方法已经标记过时。
Date我们需要学习:
-
如何将一个毫秒值(long型的数字)转成Date对象。
-
如何将一个Date对象转成long型的毫秒值。
-
Date类演示
-
Date类的构造方法
-
直接使用Date的空参数构造方法,这时是将当前系统的时间封装成Date对象。
将指定的毫秒值,封装成Date对象。
/*
* 演示Date类的创建
* 在编程语言中:操作日期的时间类,它们的时间都是距离
* 世界协调时间:1970年1月1日 零点零点零秒零毫秒
*/
public class DateDemo {
public static void main(String[] args) {
// 封装当前的系统时间
Date d = new Date();
System.out.println(d);
// 将指定的毫秒值封装成Date对象
Date d2 = new Date(1989999991232L);
System.out.println(d2);
}
}
-
Date类中的方法
将Date对象转成毫秒值。
将当前毫秒值,设置给当前的Date对象。
/*
* 演示Date类中的方法
*/
public class DateDemo2 {
public static void main(String[] args) {
Date d = new Date();
// 将Date对象转成毫秒值
long time = d.getTime();
System.out.println(time);
// 上面的两行代码相当于下面的代码
long time2 = System.currentTimeMillis();
System.out.println(time2);
// 修改当前Date封装的时间
d.setTime(12345678901234L);
System.out.println(d);
}
}
-
DateFormat类介绍
有时我们在程序中会的到字符串类型的日期格式数据:"2016年8月3日 12:12:12" 需要将这个字符串格式的日期数据转成Date对象。
有时也需要将Date数据转成字符串,并且需要转成符号人们习惯样式的字符串数据。
上面说的这个两个功能,DateFormat类就可以完成。
DateFormat类:
1、完成字符串转成Date对象
2、完成Date对象转成字符串
DateFormat类它是一个抽象类,不能直接创建对象,其中提供静态的方法可以获取到DateFormat类型的对象。
/*
* 演示DateFormat类
*/
public class DateFormatDemo {
public static void main(String[] args) {
// 获取DateFormat对象
DateFormat format = DateFormat.getDateInstance();
// 获取Date对象
Date d = new Date();
System.out.println(d);
// 将Date对象转成字符串
String s = format.format( d );
System.out.println(s);
}
}
DateFormat类,它可以将Date对象转成字符串,但是转后的字符串对应的日期的格式是固定的。并不能按照我们习惯的方式指定格式。
-
SimpleDateFormat类介绍(重点)
SimpleDateFormat 类: 它是DateFormat类的子类,SimpleDateFormat类它可以按照我们指定的格式转成日期数据。
SimpleDateFormat功能:
-
将Date对象转成字符串,这个过程被称为格式化, Date对象-------格式化---------文本(字符串) 方法 format
-
将字符串转成Date对象,这个过程被称为解析,文本(字符串) -------- 解析 ---------Date对象 方法 parse
/*
* 演示SimpleDateFormat类
*/
public class SimpleDateFormatDemo {
public static void main(String[] args) throws ParseException {
/*
* 创建SimpleDateFormat类的对象
* SimpleDateFormat( String pattern )
* 构造方法中的参数:
* 格式化日期诗句的样式:
* "yyyy" 表示的是年的四位
* "MM" 表示的月的两位
* "dd" 表示的天的两位
*
* "hh" 表示时间中的小时
* "mm" 表示的分钟
* "ss" 表示的秒
*
* 例如:我们想得到"2016年8月13日 12:12:12"
* 对应的格式: "yyyy年MM月dd日 hh:mm:ss"
*/
SimpleDateFormat sdf = new SimpleDateFormat( "yyyy年MM月dd日 hh:mm:ss" );
// 创建Date对象
Date d = new Date();
// 将Date对象格式化成字符串格式的日期数据
String time = sdf.format(d);
System.out.println(time);
// 将字符串格式的日期数据转成Date对象
String str = "2011年03-08 12:12:12";
/*
* 如果已经创建出来了SimpleDateFormat对象,但是对象格式和被转成的数据格式不同,
* 这时如果依然使用这个SimpleDateFormat对象
* 进行转成,就会发生异常
*/
Date date = sdf.parse(str);
System.out.println(date);
}
}
上面代码在运行的时候发生异常:
异常的原因:创建的SimpleDateFormat不能解析出指定字符串中的日期数据,因为字符串日期数据中的格式和SimpleDateFormat指定的格式不匹配。
解决方案:重新创建SimpleDateFormat对象,按照匹配的样式书写格式。
-
Calendar类介绍
Calendar 日历类。它本质是一个Map集合。其中保存当前时间对应的所有数据。
Calendar类它提供了可以对当前时间,年、月、日、时、分、秒的各种修改功能。并且它是一个抽象类。
可以使用Calendar类中的静态方法获取Calendar对象,其实获取的是Calendar的子类对象
/*
* 演示Calendar类的使用
*/
public class CalendarDemo {
public static void main(String[] args) {
demo4();
}
/*
* 获取任意一年的2月有多少天
* 思路:使用Calendar类,将年 设置成当前指定的年份
* 而月设置成3月,天设置1,当前的时间就会是指定的年中的3月1日,然后我们使用Calendar类中的
* add方法让这个月中的天数-1.
*/
public static void demo4() {
// 调用静态方法获取
Calendar c = Calendar.getInstance();
for( int year = 1990 ; year < 2100 ; year++ ){
// 设置时间
c.set(year, 2, 1);
// 使用add方法让月中的天数-1
c.add(Calendar.DAY_OF_MONTH, -1);
// 获取月中的天数
System.out.println(year+"年的2月中有"+c.get(Calendar.DAY_OF_MONTH) +"天");
}
}
// 修改 Calendar中的数据
public static void demo3() {
// 调用静态方法获取
Calendar c = Calendar.getInstance();
// 修改
//c.set(Calendar.YEAR, 2088);
System.out.println(c.get(Calendar.YEAR));
/*
* Calendar 中的add方法是在指定的年月日时分秒基础上 增加 或减少对应的时间
*/
c.add(Calendar.YEAR, 1);
System.out.println(c.get(Calendar.YEAR));
c.add(Calendar.DAY_OF_MONTH, 29);
System.out.println(c.get(Calendar.MONTH));
System.out.println(c.get(Calendar.DAY_OF_MONTH));
}
// 获取Calendar中的数据信息
public static void demo2() {
// 调用静态方法获取
Calendar c = Calendar.getInstance();
/*
* 使用get方法获取Calendar中封装时间信息
* get(int field)
* 由于Calendar类将所有的和时间相关的信息都封装在Map集合中。
* Map的key值是由Calendar自己命名。而在Calendar类中将封装的所有key值给我们定义静态的成员变量
*
* 注意:在计算机中,月份是从零开始,0表示1月,1表示2月,11表示12月,12表示下一年的1月
*/
int year = c.get( Calendar.YEAR );
int month = c.get( Calendar.MONTH);
int day = c.get( Calendar.DAY_OF_MONTH);
System.out.println(year + "..." + month + "..." + day);
}
// 获取Calendar对象
public static void demo1() {
// 调用静态方法获取
Calendar c = Calendar.getInstance();
/*
* 直接打印
* 通过打印的结果,发现Calendar它其实就是一个Map集合
*/
System.out.println(c);
}
}
总结:
Calendar:类中四个重要方法:
getInstance : 获取Calendar对象。
get( int field ) : 获取Calendar中封装的具体日期信息,例如:获取 年、月、日、时、分、秒各自数据
set( int field ) : 根据指定的key修改Calendar中封装的 时间 的某个信息直
set( int year , int month , int day ) 修改Calendar中封装的 年、月、日时间
add( int field , int value ) :根据指定的key修改Calendar中封装好的value值。
-
泛型技术
-
泛型引入
-
泛型:如果操作的数据类型不确定的时候,可以使用泛型来表示。不确定的数据类型。
/*
* 引入泛型:
*/
public class GenericDemo {
public static void main(String[] args) {
// 创建集合对象
List list = new ArrayList();
// 添加到集合中的任何元素都被提升成Object类型
list.add("aaa");
list.add("bbbb");
list.add("cc");
// 给容器中添加一些非String类型的数据
list.add(123);
// 遍历
for( Iterator it = list.iterator(); it.hasNext() ; ){
// 取出集合中每个元素
Object obj = it.next();
/*
* 为了能够使用String类中的length方法,必须向下转型
* 由于集合容器中可以在保存的时候,存放任意的数据类型,
* 在取出的时候统一都被提升成Object类型,那么需要使用元素
* 自身的特有方法的时候,必须向下转型,如果集合中的元素类型
* 不统一,这时向下转型,就可能发生异常。
*/
String s = (String) obj;
// 打印字符串的长度
int length = s.length();
System.out.println(length);
}
}
}
上面的代码运行发生下面的异常:
异常的原因:
由于集合中保存的元素类型不统一,在取出的时候向下转型时发生异常。
分析:
如果集合中保存的元素类型是统一的,在取出的时候还会发生吗?可是我们在定义集合的时候,并没有对集合中保存的元素的类型做任何的限制,
因此在调用添加方法的时候,可以将任何类型添加到集合中。
例如:数组,它也是容器,但是数组定义好之后,每个空间保存的数据类型就确定了,如果保存的时候,存储的类型不一致就会在编译的时候报错。
int[] arr = new int[4];
arr[0] = 123;
arr[1] = 456;
arr[2] = 3.14; // 这行代码在编译的时候就会报错。
我们借助数组这种方案:在定义容器的时候,就限定容器中的数据类型,在保存的时候,如果类型不匹配,就不让程序编译通过。如果程序编译通过,就说明元素类型一定是统一的。取出的时候,肯定不会发生类型转换异常。
针对集合,要解决这个限定数据类型, 我们指定通过Java中的泛型技术。
-
泛型技术介绍
泛型的书写格式:
<具体的数据类型> 。
注意:泛型中的数据类型,必须是引用类型,不能是基本类型。
总结:泛型它是一种技术,主要是用来限定数据类型的。
-
泛型简单应用(理解泛型作用)
/*
* 演示泛型具体代码中应用
*/
public static void method1() {
// 演示泛型的应用
TreeSet set = new TreeSet( new MyComparator());
Collections.addAll(set, "AAAAAA", "aa", "bbb", "a", "BBBB");
System.out.println(set);
}
在比较器上添加泛型之后,发现在方法中的参数都会变成指定的数据类型。
public class MyComparator implements Comparator<String>{
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
}
publicstaticvoid method2() {
/*
* 演示泛型的应用
* jdk7:菱形技术 :引用中已经明确具体的泛型类型,在创建对象的时候可以省略类型
* jdk8: lambda 表达式 λ
TreeSet<String> set = new TreeSet<String>( new Comparator<String>(){
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
} );
上面使用匿名内部类中仅仅只是为了实现接口中的方法,并且接口中只有一个方法,所以可以直接使用lambda表达式代替
代替的原则:只要是接口中固定的内容都可以省略,而需要程序书写的东西留下。
TreeSet<String> set = new TreeSet<String>( ( o1 , o2) -> {
return o1.length() - o2.length();
});
*/
TreeSet<String> set = new TreeSet<String>( ( s1 , s2)->s1.length() - s2.length() );
Collections.addAll(set, "AAAAAA", "aa", "bbb", "a", "BBBB");
System.out.println(set);
}
-
自定义泛型
泛型中的两个概念:
-
使用泛型
有时我们在使用某个类或接口的时候,这个类或接口上定义<> ,这时我们在使用的时候,就需要明确<>中需要表示的具体的数据类型。
这时我们相当于在使用泛型。
例如:在集合中大量的类和接口,而这些类和接口本身是由sun公司定义好。而sun公司在定义集合类和接口的时候,它们在类和接口上声明<>.
然后我们在使用的时候需要明确<>中的数据类型。
-
定义泛型
既然sun公司在定义类和接口的时候可以在类和接口上定义(声明)<> (泛型)。
那么我们自己在定义类和接口的时候也可以模拟sun公司这种定义方式。
-
泛型类(会定义即可)
泛型类:在定义类的时候,在类上定义泛型。
普通的类的定义格式:
修饰符 class|interface 类名|接口名{
}
泛型类的定义格式:
修饰符 class 类名<标识符>{
}
/*
* 演示 自定义泛型在类上
*
* 在定义的类时候,可以在类上使用<> 明确需要定义的泛型
* 定义的泛型名称是任意合法的标识符,一般建议泛型名称字母全部大写。
*/
// 定义类和泛型
class Generic<T>{
}
public class Demo {
public static void main(String[] args) {
// 使用类
Generic<Integer> g = new Generic<Integer>();
}
}
s
泛型类的简单使用:
1、在定义类的时候定义了泛型,在使用类的时候明确泛型的具体数据类型。
2、如果在类上定义的泛型,在类中的任何地方都可以使用。
// 定义类和泛型
class Generic<T>{
private T t ;
public void demo( T t){
System.out.println(t);
}
}
3、类上的泛型,在定义类对象的时候会明确,在类中所有引用这个泛型的地方,全部都会被明确的数据类型代替。
在创建对象的时候,明确泛型的类型是Integer类型,类中所有有T类型的地方,全部都会被Integer代替。
-
泛型方法(会定义即可)
类上定义的泛型,在类中是可以直接使用,但是有时会出现方法上需要数据类型和类上明确的泛型的类型不一致。
如果方法上参数类型和类上定义的泛型的类型不一致,这时我们可以单独在这个方法上定义泛型(泛型方法)。
普通方法的定义格式:
修饰符 返回值类型 方法名( 参数类型 参数名 , 参数类型 参数名 ){
}
泛型方法的定义格式:
修饰符 <M> 返回值类型 方法名( M 参数名 , M 参数名 ){
}
定义的泛型方法:泛型的定义必须在方法的返回值类型前面书写。在方法上定义的泛型,在方法的参数以及方法体,返回值上都可以使用。
定义在方法上的泛型:是在调用这个方法的时候根据传递的那个具体的数据的类型确定。
细节:静态方法不能使用类上的泛型。
原因:静态方法不需要当前类的对象就可以直接调用。而类上定义的泛型,必须是在创建这个类的对象的时候来明确泛型的具体类型。
注意:静态方法如果要使用泛型,只能在静态方法上定义自己的泛型。
-
泛型接口&传递(会定义即可)
同样也可以将泛型定义在接口上。
定义格式:
修饰符 interface 接口名<标识符>{
}
/*
* 演示接口上定义泛型
*/
interface Inter<N>{
}
// 定义接口的实现类
class InterImpl implements Inter<String>{
}
接口上定义的泛型,在实现这个接口的时候可以明确。
泛型的传递:
interface Collection<E>{
}
interface List<E> extends Collection<E>{
}
class ArrayLlist<E> implements List<E>{
}
最后我们在使用ArrayList集合容器:
ArrayList<String> list = new ArrayList<String>();
-
泛型限定
-
泛型通配符
-
通配符:使用某个符号来代表更多的含义或数据。这个符号称为通配符。
/*
* 演示泛型的通配符:泛型中使用 ? 问号 作为通配的符号。
*/
public class Demo3 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
// 添加元素
Collections.addAll(list, "aaa","bbb","ccc","dddd");
printList(list);
LinkedList<Integer> list2 = new LinkedList<Integer>();
// 添加元素
Collections.addAll(list2, 123,456,789,111,222,333);
printList(list2);
}
/*
* 将上面的打印集合的方法进行抽取,专门定义一个方法用来遍历集合容器
* 下面的方法可以接收任意一个List集合容器,但是接收的集合容器中的数据类型无法确定
* 因为我们的方法进行是负责打印任何的List集合容器,至于调用者会传递具体List下的那个集合我们不确定
* 并且集合中的元素更无法确定,因此我们可以使用List类型来接收List下的任何集合容器,
* 而容器中的元素可以使用泛型的通配符表示 ?
* ? 就表示可以接收任意的List下的集合容器,并且对集合中的元素没有任何的限制。
*/
public static void printList( List<?> list ){
for (Iterator<?> it = list.iterator(); it.hasNext();) {
System.out.println(it.next());
}
}
}
-
泛型限定(理解限定格式)
publicclass Demo4 {
publicstaticvoid main(String[] args) {
ArrayList<String> list3 = new ArrayList<String>();
// 添加元素
Collections.addAll(list3, "aaa", "bbb", "ccc", "dddd");
//printList(list3);
ArrayList<Student> list = new ArrayList<Student>();
// 添加元素
list.add(new Student("华安", 19));
list.add(new Student("石榴", 88));
list.add(new Student("华夫人", 100));
printList(list);
LinkedList<Teacher> list2 = new LinkedList<Teacher>();
// 添加元素
list2.add(new Teacher("班导", 18));
list2.add(new Teacher("波波老师", 38));
list2.add(new Teacher("锁哥", 48));
list2.add(new Teacher("唐老师", 35));
printList(list2);
}
/*
* 希望printList方法可以接收List下的任何集合容器:方法上只要使用List接口来接收
* 同时还希望方法上接收的集合容器中的元素类型必须是Person或Person的子类。
* 这时泛型就不能仅仅只使用通配符了,必须对这个通配符进行限定:
* 泛型限定:
* 上限限定: ? extends E ?表示的类型可以是E类型或E的子类类型
* 例如: ? extends Person 此时的? 代表的元素类型只能是Person或Person的子类
* 下限限定: ? super E ?表示的类型是E类型或E的父类类型,但E不能是E的子类,或E的兄弟类型
* 例如:? super Student
* 此时的?号 可以是 Student,或Student的父类(Person、Object),但不能Teacher,或Student的子类
*/
publicstaticvoid printList(List< ? extends Person> list) {
for (Iterator<?> it = list.iterator(); it.hasNext();) {
System.out.println(it.next());
}
}
}
public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)
-
泛型总结:
泛型定义格式:
类名或接口 < 标识符 >
泛型使用:
在new对象的时候,明确具体的泛型类型
泛型类:
定义在类上的泛型,需要在创建这个类的对象的时候明确具体的类型。同时定义在类上的泛型,可以在类中直接引用。
泛型方法:
如果方法上需要的数据类型和类上泛型表示的数据类型不一致,这时可以在方法的返回值前面定义当前这个方法自己的泛型
泛型接口:
定义接口的使用定义泛型,而这个泛型需要在实现这个接口,或者将这个泛型传递给子类,最后由使用这个子类的程序明确泛型的类型
泛型通配符:?
泛型的限定:
? extends E 上限限定
? super E 下限限定
泛型属于编译时期的技术,在编译的时候,编译会根据指定的泛型类型检查程序中的类型是否一致。而如果编译通过,泛型全部会被删除。因此将泛型这个特点:泛型擦除技术。
-
IO技术
-
IO技术介绍
-
从第一天到现在,我们编写的程序中的数据都保存在计算机的内存中。而计算机的内存它属于临时存储设备。这样的程序无法长久的保存数据。而我们更希望数据可以长久的保存,每次运行的结果都可以存储起来,下次程序运行的时候可以继续使用上次的结果数据。
为解决上面的问题,可以持久保存数据,java中给出了IO技术。
IO技术:
I : input 输入,读取
O : output 输出, 写出。
Java中的IO技术,主要是将Java程序中的数据通过输出方式,将数据保存到磁盘等持久设备上。同时还可以从持久设备读取保存的数据。
持久设备:硬盘、U盘、光盘、磁带、云盘等。
输入、输出的方向:
以内存为中心,只要是其他地方的数据进内存,我们都称为输入。只要是数据从内存中到其他的地方都称为输出。
-
IO的学习路线
IO:必须搞清楚IO的方向问题。
File类学习:它是专门负责操作文件或文件夹
字节流学习:
字符流学习:
编码表学习:
缓冲区学习:
转换流学习:
功能流:了解
GUI:图形化界面程序。
-
File类
我们的持久设备中最终都会保存对应的数据。而这些数据最终都是以文件的形式体现。
在持久设备上,文件是用来保存真正具体的数据的。
而文件夹它是用来方便我们管理不同的文件或文件夹。
在持久设备上会有文件和文件夹这两类特殊的保存数据的事物。既然在持久设备中有它们,Java就一定会通过类封装这种特殊的事物。
这个类就是File类。
由于我们学习的Java中的IO技术,因此这些类和接口都保存在java.io包下。
在API中说的目录路径,其实就是我们平时操作的文件夹。
以后只要在Java中需要操作文件或文件夹,统一使用File类完成。
例如:创建文件(夹)、删除文件(夹)、遍历硬盘上的文件或文件夹等操作,这些都是File类负责。
/*
* 简单演示File类中获取系统的所有根目录
*/
public class FileDemo {
public static void main(String[] args) {
// 使用File类中的静态方法
File[] roots = File.listRoots();
// 遍历数组
for (File file : roots) {
System.out.println(file);
}
}
}