文章目录

  • String类
  • String的方法
  • StringBuffer类
  • StringBuffer的方法
  • 为什么StringBuffer比String拼接快?
  • StringBuilder类
  • Scanner类
  • Scanner类的方法
  • Arrays中的方法(针对数组的操作,不是集合)
  • Java中各种基本数据类型包装类
  • BigDecimal类
  • BigDecimal类的方法
  • Math类
  • Math类的方法
  • Java中的日期表示---Date,Calendar类和日期格式化
  • JDK8新增的时间管理API
  • java中的集合类接口
  • Collection接口方法
  • list类型
  • 集合类ArrayList
  • ArrayList的方法
  • 集合类LinkedList
  • 集合类Vector
  • set类型
  • 集合类HashSet
  • 集合类LinkedHashSet
  • 集合类TreeSet
  • hashCode()和equals()的重写
  • 重写equals() 方法的基本原则
  • Collections工具类
  • ArrayList/LinkedList/Vector的异同?
  • Java中的iterator接口
  • Java中的Map
  • HashMap
  • HashMap在JDK7中的底层实现原理
  • Map接口的方法
  • HashMap及源码分析
  • TreeMap
  • Hashtable
  • Properties
  • Java中的范型
  • Java中如何创建使用范型的类?
  • Java中的可变参数
  • JUnit基本测试
  • JUnit注解:
  • JUnit中常用的注解
  • Java中的File
  • File类中的方法
  • Java中的IO流
  • 数据流
  • FileInputStream
  • FileOutputStream
  • 缓冲区字节输出流
  • BufferedOutputStream
  • BufferedInputStream
  • 转换流
  • 字符输出流
  • OutputStreamWriter
  • InputStreamReader
  • 字符缓冲区流
  • 打印流
  • 数据流
  • 对象流
  • serialVersionUID
  • 随机存取文件流
  • 类结构(部分)
  • java中的枚举类型
  • 枚举遍历
  • java中类的加载机制
  • Java中的反射
  • Class中的方法
  • 字段(Field类的方法)
  • 方法(Method类的方法)


String类

String:字符串,使用一对“”引起来表示。
  1.String声明为final的,不可被继承
  2.String实现了Serializable接口:表示字符串是支持序列化的。
  实现了Comparable接口:表示String可以比较大小
  3.String内部定义了final char[] value用于存储字符串数据
  4.String:代表不可变的字符序列。简称:不可变性。
  体现:1.当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。
  2.当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
  3.当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
  5.通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。
 6.字符串常量池中是不会存储相同内容的字符串的。

没有使用new,则直接指向常量池,有使用new,必定经过堆

java将绝对路径改为资源目录_Java


String对象拼接可参考

1.常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量。
    2.只要其中有一个是变量,结果就在堆中
    3.如果拼接的结果调用intern()方法,返回值就在常量池中

常量池位置:

java将绝对路径改为资源目录_java将绝对路径改为资源目录_02


String创建出来的实例时不可修改的,除非实在字符串缓存区创建的字符串可以修改,所有String的实例时可以共享的

String的方法

String 类包括的方法可用于检查序列的单个字符、比较字符串、搜索字符串、提取子字符串、创建字符串副本并将所有字符全部转换为大写或小写。
String类型在JDK9之前使用char保存,在9之后使用byte保存,节约了内存
string.length():获取字符串的长度
string.index(String s):返回子字符串出现的位置,-1未找到
string.index(int ch):同上,数字表示字符,具体查看ASCLL,int和char类型可以互相转化
string.lastIndexOf(String s):从后向前查找,返回子字符串出现的位置,-1未找到
string.charAt(int i):获取指定为位置字符,索引超出范围会报错
string.substring(int beginindex):获取子字符串,从输入索引到最后得子字符串
string.substring(int beginindex ,int endindex):同上,有设置结束索引得截取方式
string.startsWith(String prefix):判断字符是否串以什么开头
string.endsWith(String suffix):判断字符串是否以什么结尾
string.equals(String s):比较字符串是否相等
string.equalsIgnoreCase(String s):比较字符串是否相等,忽略大小写
string.compareTo(String s):比较字符串大小,用于字符串排序
string.contains(String s):判断是否包含
string.isEmpty():判断字符串是否为空,不是是否为null,而是""
string.toUpperCase():返回字符串大写表示
string.toLowerCase():返回字符串小写表示
string.split(String regex):字符串切割,返回字符串数组
string.replace(String old, String new):字符串替换,也可以用字符
string.trim():去除两边空格
string.intern():进入常量池
string.toCharArray():得到字节数组

StringBuffer类

可变字符串
主要操作是 append 和 insert 方法,可接收任意类型数据

StringBuffer字符串的字符串缓冲区初始容量为 16 个字符
StringBuffer初始的总字符串容量为字符串长度 + 16
新建StringBuffer时可以设置缓冲区大小
如果容量被占慢,StringBuffer会申请一个是原来总容量两倍多一点的新空间,将内容复制到新空间,将引用地址修改为新空间,原来的空间就废弃了

StringBuffer的方法

append():字符串拼接,返回拼接后的自身,可以实现链式编程

delete(int start, int end):删除对应位置的字符串
capacity() :可以获取当前容量

为什么StringBuffer比String拼接快?

StringBuffer在创建的时候,会申请一块较大的空间,具体多大由存入的字符串决定,如果需要拼接字符串,只要把新的字符串复制到已经申请的空间里就可以了,而String申请只会申请字符串存储需要得空间,没有多余空间,拼接字符串需要申请空间,申请空间就需要时间。
StringBuffer创建时使用得字符串称为原序列,而后增加的序列为缓冲区序列,该类在缓冲区序列上同步

StringBuilder类

方法和功能和StringBuffer几乎一样
StringBuilder性能略高于StringBuffer,但是线程不安全
StringBuffer虽性能不及StringBuilder,但是线程安全

Scanner类

Scanner是一个简单的文本扫描器,可以对输入数据进行一些简单的格式化
Scanner不仅仅可以用System作为参数,字符串也可以

Scanner类的方法

next()获取输入的字符串
nextLine()表示获取一行
nextInt()将输入转换成int获取,如果失败,抛出异常
hasNextInt()下一个是否是整数,是true,否false
同样的也有其它类型的方法,例如double、float、byte、short、long类型,还有BigDecimal和BigInteger
hasNext()是否有输入

Arrays中的方法(针对数组的操作,不是集合)

Arrays.fill(int[] a,int i)填充数组
Arrays.binarySearch():二分搜索,前提是数组必须有序

Java中各种基本数据类型包装类

int Integer
float Float
double Double
char Character
boolean Boolean

如果将基本类型赋值给对应的包装类,会自动装箱,反之拆箱
简单介绍下Integer,其他包装类的方法基本是一样的
parseInt():将字符串转换为int(10进制)类型
valueOf():将参数转换为Integer类型

如果存的数字很大,大到连long类型都存不下,可以使用BigInteger:
这个包放在 java.math.BigInteger底下
这个类的参数为字符串,且这个类没有整数范围。

BigDecimal类

精度高的浮点类型数字,初始化时最好使用字符串形式的数字或者是使用valueOf方法
在Java里,float和double在小数点后几位都是不精确的,如果直接使用double类型转换,转换后的数值就会不精确(例如0.1),如果使用valueOf方法,会在数据类型的精度上进行调整,但最好还是使用字符串进行转换
BigDecimal由于是引用类型的数据,所以加减乘除的时候不可以直接使用 + - * /,必须要用方法取代,比如add(),返回值依然为BigDecimal:

BigDecimal类的方法

add():加
multiply():乘
divide():除
subtract():减

Math类

包含一些简单的数学运算方法,如初等指数、对数、平方根和三角函数。

Math类的方法

abs():绝对值
round():四舍五入
squrt():平方根
pow(int x, int y):x的y次方
ceil():向上取整
floor():向下取整
max():从两个值里取最大值
min():从两个值里取最小值
random():成成随机数范围[0,1)

Java中的日期表示—Date,Calendar类和日期格式化

Date有util包下的,也有sql包下的,二者的toString方法不同:

Date date1 = new Date();
System.out.println(date1.toString());   //Sat May 09 20:09:11 CST 2020


java.sql.Date date3 = new java.sql.Date(35235325345L);
System.out.println(date3);  //1971-02-13

Date被发现有一些缺陷,所以又有了Calendar类,可以看作升级版本
Calendar使用的是单例模式,Date不是
Date可以使用SimpleDateFormat格式化时间,使用参数:“yyyy-MM-dd HH:mm:ss”
Date可以调用方法查看年月日。
Calendar类种可以查看的信息比Date多
Calendar中定义了很多有用的字段,可以直接通过类来获取而不需要去转换

可以使用方法对时间经行修改
getTime()方法可以获得Date实例
Date类的API不易于国际化,大部分被废弃了,java.text.SimpleDateFormat类是一个不与语言环境有关的方式来格式化和解析日期的具体类。

Date date = new Date();
System.out.println(date);   //Sun May 10 16:34:30 CST 2020

String format = sdf.format(date);
System.out.println(format); //20-5-10 下午4:34

//解析:格式化的逆过程,字符串---》日期
String str = "19-12-18 上午11:43";
Date date1 = sdf.parse(str);
System.out.println(date1);  //Wed Dec 18 11:43:00 CST 2019

SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd aaa hh:mm");
//格式化
String format1 = sdf1.format(date);
System.out.println(format1);    //2020-05-14 下午 02:29
//解析:要求字符串必须是符合SimpleDateFormat识别的格式(通过构造器参数体现),
//否则,抛异常
Date date2 = sdf1.parse("2020-05-15 下午 05:28");
System.out.println(date2);  //Fri May 15 17:28:00 CST 2020
JDK8新增的时间管理API

本地日期(LocalDate)、本地时间(LocalTime)、本地日期时间(LocalDateTime)、时区(ZonedDateTime)和持续时间(Duration)的类

LocalDate localDate = LocalDate.now();
LocalTime localTime = LocalTime.now();
LocalDateTime localDateTime = LocalDateTime.now();

System.out.println(localDate);//2020-05-14 
System.out.println(localTime);//14:47:32.933 
System.out.println(localDateTime);//2020-05-14T14:47:32.933

//of():设置指定的年、月、日、时、分、秒。没有偏移量
LocalDateTime localDateTime1 = LocalDateTime.of(2020, 10, 6, 13, 23, 43);
System.out.println(localDateTime1);//2020-10-06T13:23:43

//LocalDateTime的方法模式
//getXxx():获取相关的属性
//withXxx():设置相关的属性
//plusXxx():增加数值,比如增加3天(会产生新对象)
//minusXxx():减少数值,比如减少3天(会产生新对象)

//例如:
//System.out.println(localDateTime.getMonth());
//LocalDate localDate1 = localDate.withDayOfMonth(22);
//LocalDateTime localDateTime4 = localDateTime.minusDays(6);

java将绝对路径改为资源目录_java将绝对路径改为资源目录_03


Instant类:时间线上的一个瞬时点。这可能被用来记录应用程序中的事件时间戳。

java将绝对路径改为资源目录_字符串_04

//now():获取本初子午线对应的标准时间
Instant instant = Instant.now();
System.out.println(instant);    //2020-05-14T06:57:03.145Z

//添加时间的偏移量
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));//东八区
System.out.println(offsetDateTime); //2020-05-14T14:57:03.145+08:00

//toEpochMilli():获取自1970年1月1日0时0分0秒(UTC)开始的毫秒数  ---> Date类的getTime()
long milli = instant.toEpochMilli();
System.out.println(milli);  //1589104867591

//ofEpochMilli():通过给定的毫秒数,获取Instant实例  -->Date(long millis)
Instant instant1 = Instant.ofEpochMilli(1550475314878L);
System.out.println(instant1);   //2019-02-18T07:35:14.878Z

DateTimeFormatter的使用

java将绝对路径改为资源目录_字符串_05

//方式一:预定义的标准格式。如:ISO_LOCAL_DATE_TIME;ISO_LOCAL_DATE;ISO_LOCAL_TIME
DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
//格式化:日期-->字符串
LocalDateTime localDateTime = LocalDateTime.now();
String str1 = formatter.format(localDateTime);
System.out.println(localDateTime);
System.out.println(str1);//2020-05-14T15:10:39.017

//解析:字符串 -->日期
TemporalAccessor parse = formatter.parse("2020-05-14T15:10:39.017");
System.out.println(parse);

//方式二:
//本地化相关的格式。如:ofLocalizedDateTime()
//FormatStyle.LONG / FormatStyle.MEDIUM / FormatStyle.SHORT :适用于LocalDateTime
DateTimeFormatter formatter1 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);
//格式化
String str2 = formatter1.format(localDateTime);
System.out.println(str2);//2020年5月14日 下午03时10分39秒

//本地化相关的格式。如:ofLocalizedDate()
//FormatStyle.FULL / FormatStyle.LONG / FormatStyle.MEDIUM / FormatStyle.SHORT : 适用于LocalDate
DateTimeFormatter formatter2 = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM);
//格式化
String str3 = formatter2.format(LocalDate.now());
System.out.println(str3);//2020-5-14


//重点: 方式三:自定义的格式。如:ofPattern(“yyyy-MM-dd hh:mm:ss”)
DateTimeFormatter formatter3 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
//格式化
String str4 = formatter3.format(LocalDateTime.now());
System.out.println(str4);//2020-05-14 03:10:39

//解析
TemporalAccessor accessor = formatter3.parse("2020-05-14 03:10:39");
System.out.println(accessor);

ZoneId:

//ZoneId:类中包含了所有的时区信息
// ZoneId的getAvailableZoneIds():获取所有的ZoneId
Set<String> zoneIds= ZoneId.getAvailableZoneIds();
for(String s: zoneIds) {
	System.out.println(s);
}
// ZoneId的of():获取指定时区的时间
LocalDateTime localDateTime= LocalDateTime.now(ZoneId.of("Asia/Tokyo"));
System.out.println(localDateTime);

//ZonedDateTime:带时区的日期时间
// ZonedDateTime的now():获取本时区的ZonedDateTime对象
ZonedDateTime zonedDateTime= ZonedDateTime.now();
System.out.println(zonedDateTime);
// ZonedDateTime的now(ZoneId id):获取指定时区的ZonedDateTime对象
ZonedDateTime zonedDateTime1= ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
System.out.println(zonedDateTime1);

Duration时间间隔:

//Duration:用于计算两个“时间”间隔,以秒和纳秒为基准
LocalTime localTime= LocalTime.now();
LocalTime localTime1= LocalTime.of(15, 23, 32);
System.out.println(localTime);
System.out.println(localTime1);
//between():静态方法,返回Duration对象,表示两个时间的间隔
Duration duration= Duration.between(localTime1, localTime);
System.out.println(duration);//PT12M55.901S

System.out.println(duration.getSeconds());//775
System.out.println(duration.getNano());//901000000

Period日期间隔:

//Period:用于计算两个“日期”间隔,以年、月、日衡量
LocalDate localDate= LocalDate.now();
LocalDate localDate1= LocalDate.of(2028, 3, 18);

Period period= Period.between(localDate, localDate1);
System.out.println(period);System.out.println(period.getYears());

System.out.println(period.getMonths());
System.out.println(period.getDays());

java中的集合类接口

Java 集合可分为Collection 和Map 两种体系

Collection接口:单列数据,定义了存取一组对象的方法的集合

  • List:元素有序、可重复的集合
  • Set:元素无序、不可重复的集合

Map接口:双列数据,保存具有映射关系“key-value对”的集合

Collection接口方法

Collection 接口是List、Set 和Queue 接口的父接口,该接口里定义的方法既可用于操作Set 集合,也可用于操作List 和Queue 集合。
JDK不提供此接口的任何直接实现,而是提供更具体的子接口(如:Set和List)实现。
在Java5 之前,Java 集合会丢失容器中所有对象的数据类型,把所有对象都当成Object 类型处理;从JDK 5.0 增加了泛型以后,Java 集合可以记住容器中对象的数据类型。

ArrayList: 作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[] elementData存储
LinkedList: 对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储
Vector: 作为List接口的古老实现类;线程安全的,效率低;底层使用Object[] elementData存储

list类型
集合类ArrayList

如果实例化时调用无参构造器,默认有10的容量

jdk7中的ArrayList的对象的创建类似于单例的饿汉式,而jdk8中的ArrayList的对象 的创建类似于单例的懒汉式,延迟了数组的创建,节省内存。

ArrayList的方法

add():添加一个数据,如果没有使用范型则为Object类型,也可以在指定位置插入一个数据

addAll():可以传递一个集合对象,如果觉得一个一个传递慢的话

get():索取某个索引的值

remove():按照数据删除,或者是用索引删除,但如果数据是int型,则只能使用索引删除

removeAll():按照集合删除

contains():判断是否包含某个数据,参数不能是集合

toArray():将ArrayList转换为Object数组

retainAll(Collection c):取两个集合的交集

iterator():获取集合的迭代器,可以使用集合器进行迭代,迭代过程不用临时索引
迭代器是一次性的,使用完后必须重新获得 ,迭代器是快速失败的
快速失败表示迭代器迭代时,集合如果改变,将会出现异常,ArrayList的迭代器的迭代器并不是拷贝数据迭代,而是直接观察集合内的值,所以观察使修改值会抛出异常(多线程),而安全失败则是拷贝

while(iterator.hasNext()) {
	System.out.println(iterator.next());
}
集合类LinkedList
  • 对于频繁的插入或删除元素的操作,建议使用LinkedList类,效率较高
  • LinkedList:双向链表,内部没有声明数组,而是定义了Node类型的first和last,用于记录首末元素。同时,定义内部类Node,作为LinkedList中保存数据的基本结构
集合类Vector

Vector 是一个古老的集合,JDK1.0就有了。大多数操作与ArrayList相同,区别之处在于Vector是线程安全的。
在各种list中,最好把ArrayList作为缺省选择。当插入、删除频繁时,使用LinkedList;Vector总是比ArrayList慢,所以尽量避免使用。

使用起来和ArrayList差不多,Vector是线程安全的,性能比ArrayList低
集合类还有LinkedList,有addFirst()和addLast()方法,存储结构上和ArrayList不一样,ArrayList使用的是数组形式,而LinkedList使用的是链表,适合数据频繁插入删除

set类型
HashSet:作为Set接口的主要实现类;线程不安全的;可以存储null值
LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历
对于频繁的遍历操作,LinkedHashSet效率高于HashSet.
TreeSet:可以按照添加对象的指定属性,进行排序。
集合类HashSet

HashSet是Set 接口的典型实现,大多数时候使用Set 集合时都使用这个实现类。
HashSet按Hash 算法来存储集合中的元素,因此具有很好的存取、查找、删除性能。
HashSet具有以下特点:不能保证元素的排列顺序
HashSet不是线程安全的
集合元素可以是null
HashSet 集合判断两个元素相等的标准:两个对象通过hashCode() 方法比较相等,并且两个对象的equals() 方法返回值也相等。
对于存放在Set容器中的对象,对应的类一定要重写equals()和hashCode(Object obj)方法,以实现对象相等规则。即:“相等的对象必须具有相等的散列码”。

内部是无序的,就是遍历时不会按照添加顺序输出,且添加的数据时不会重复的,HashSet中实际上时维护一个HashMap。
HashSet中不能使用get()按照索引获得数据,因为数据无序,即使有方法也没有意义

初始大小为16

底层:数组+链表的结构。

存放是计算过HashCode,结果决定存放位置,如果有冲突,调用equals判断是否数据一致,一致则添加失败

集合类LinkedHashSet

LinkedHashSet是HashSet的子类
LinkedHashSet根据元素的hashCode值来决定元素的存储位置,但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
LinkedHashSet插入性能略低于HashSet,但在迭代访问Set 里的全部元素时有很好的性能。
LinkedHashSet不允许集合元素重复。

集合类TreeSet

与HashSet对应的,有一个TreeSet,也是无序且数据唯一
HashSet内部依靠Hash算法存储
TreeSet内部依靠树结构存储

TreeSet底层使用红黑树结构存储数据

TreeSet两种排序方法:自然排序和定制排序。默认情况下,TreeSet采用自然排序。

判断两个元素相等的标准是:通过Comparator比较两个元素返回了0

特点:有序,查询速度比List快

hashCode()和equals()的重写

重写hashCode() 方法的基本原则
在程序运行时,同一个对象多次调用hashCode() 方法应该返回相同的值。
当两个对象的equals() 方法比较返回true 时,这两个对象的hashCode() 方法的返回值也应相等。
对象中用作equals() 方法比较的Field,都应该用来计算hashCode 值。

重写equals() 方法的基本原则

当一个类有自己特有的“逻辑相等”概念,当改写equals()的时候,总是要改写hashCode(),根据一个类的equals方法(改写后),两个截然不同的实例有可能在逻辑上是相等的,但是,根据Object.hashCode()方法,它们仅仅是两个对象。
因此,违反了“相等的对象必须具有相等的散列码”。
结论:复写equals方法的时候一般都需要同时复写hashCode方法。通常参与计算hashCode的对象的属性也应该参与到equals()中进行计算。

Collections工具类

Collections工具类
操作数组的工具类:Arrays

Collections 是一个操作Set、List和Map 等集合的工具类

Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法

排序操作:(均为static方法)

reverse(List):反转List 中元素的顺序
shuffle(List):对List集合元素进行随机排序
sort(List):根据元素的自然顺序对指定List 集合元素按升序排序
sort(List,Comparator):根据指定的Comparator 产生的顺序对List 集合元素进行排序
swap(List,int,int):将指定list 集合中的i处元素和j 处元素进行交换

ArrayList/LinkedList/Vector的异同?

ArrayList和LinkedList的异同二者都线程不安全,相对线程安全的Vector,执行效率高。

此外,ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。

对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。

对于新增和删除操作add(特指插入)和remove,LinkedList比较占优势,因为ArrayList要移动数据。

ArrayList和Vector的区别: Vector和ArrayList几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized),属于强同步类。

因此开销就比ArrayList要大,访问要慢。

因为同步完全可以由程序员自己来控制。Vector每次扩容请求其大小的2倍空间,而ArrayList是1.5倍。Vector还有一个子类Stack。

Java中的iterator接口

Collection接口继承了java.lang.Iterable接口,该接口有一个iterator()方法,那么所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象。
Iterator 仅用于遍历集合,Iterator本身并不提供承装对象的能力。如果需要创建Iterator 对象,则必须有一个被迭代的集合。
集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前。

游标:初始位置为-1

remove():删除当前游标处的数据

hasNext():判断游标后一个是否有数据

next():游标向后移动,并返回当前指向数据

Java中的Map

HashMap集合(提供键值对映射,使用Hash算法):
键的值不允许重复,重复值会覆盖

HashMap:作为Map的主要实现类;线程不安全的,效率高;存储null的key和value
LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历。
原因:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。
对于频繁的遍历操作,此类执行效率高于HashMap。
TreeMap:保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序
底层使用红黑树
Hashtable:作为古老的实现类;线程安全的,效率低;不能存储null的key和value
Properties:常用来处理配置文件。key和value都是String类型

Map中存储的key-value的特点

  • Map与Collection并列存在。用于保存具有映射关系的数据:key-value
  • Map 中的key 和value 都可以是任何引用类型的数据
  • Map 中的key 用Set来存放,不允许重复,即同一个Map 对象所对应的类,须重写hashCode()和equals()方法
  • 常用String类作为Map的“键”
  • key 和value 之间存在单向一对一关系,即通过指定的key 总能找到唯一的、确定的value
  • Map接口的常用实现类:HashMap、TreeMap、LinkedHashMap和Properties。其中,HashMap是Map 接口使用频率最高的实现类
HashMap
  • HashMap是Map 接口使用频率最高的实现类。
  • 允许使用null键和null值,与HashSet一样,不保证映射的顺序。
  • 所有的key构成的集合是Set:无序的、不可重复的。所以,key所在的类要重写:equals()和hashCode()
  • 所有的value构成的集合是Collection:无序的、可以重复的。所以,value所在的类要重写:equals()
  • 一个key-value构成一个entry
  • 所有的entry构成的集合是Set:无序的、不可重复的
  • HashMap 判断两个key 相等的标准是:两个key 通过equals() 方法返回true,hashCode值也相等。
  • HashMap判断两个value相等的标准是:两个value 通过equals() 方法返回true。

HashMap是数组+链表+红黑树实现。

HashMap在JDK7中的底层实现原理
/*
 * HashMap的底层实现原理?以jdk7为例说明:
 *    HashMap map = new HashMap():
 *    在实例化以后,底层创建了长度是16的一维数组Entry[] table。
 *    ...可能已经执行过多次put...
 *    map.put(key1,value1):
 *    首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置。
 *    如果此位置上的数据为空,此时的key1-value1添加成功。 ----情况1
 *    如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据
 *    的哈希值:
 *           如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功。----情况2
 *           如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)方法,比较:
 *                如果equals()返回false:此时key1-value1添加成功。----情况3
 *                如果equals()返回true:使用value1替换value2。
 *
 *   补充:关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储。
 *
 *   在不断的添加过程中,会涉及到扩容问题,当超出临界值(且要存放的位置非空)时,扩容。默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来。
 *
 */
Map接口的方法

put():添加键值对
putAll():添加一个map集合
get():通过键获得值
keySet():获取map中的key集合
valueSet():获取map中的value集合
contains():判断是否包含某个键
containsValue():判断是否包含某个值

TreeMap和HashMap类似
HashTable和HashMap类型,HashTable为线程安全,但是效率低下

HashMap及源码分析


TreeMap

TreeMap存储Key-Value 对时,需要根据key-value 对进行排序。TreeMap可以保证所有的Key-Value 对处于有序状态。

TreeSet底层使用红黑树结构存储数据

TreeMap的Key 的排序:

自然排序:TreeMap的所有的Key 必须实现Comparable 接口,而且所有的Key 应该是同一个类的对象,否则将会抛出ClasssCastException
定制排序:创建TreeMap时,传入一个Comparator 对象,该对象负责对TreeMap中的所有key 进行排序。此时不需要Map 的Key 实现Comparable 接口
TreeMap判断两个key相等的标准:两个key通过compareTo()方法或者compare()方法返回0

Hashtable

Hashtable是个古老的Map 实现类,JDK1.0就提供了。不同于HashMap,Hashtable是线程安全的。
Hashtable实现原理和HashMap相同,功能相同。底层都使用哈希表结构,查询速度快,很多情况下可以互用。
与HashMap不同,Hashtable不允许使用null 作为key 和value
与HashMap一样,Hashtable也不能保证其中Key-Value 对的顺序
Hashtable判断两个key相等、两个value相等的标准,与HashMap一致。

Properties

Properties 类是Hashtable的子类,该对象用于处理属性文件
由于属性文件里的key、value 都是字符串类型,所以Properties 里的key 和value 都是字符串类型
存取数据时,建议使用setProperty(String key,Stringvalue)方法和getProperty(String key)方法

Java中的范型

范型可以用在集合类型的数据里,比如:

ArrayList<String> arrayList = new ArrayList<String>();
Java中如何创建使用范型的类?

在类名后添加上< T >即可

public class My<T>{
	private T = (T[])new Object[10];
}

可以创建一个引用使用通配符 ?,但是不能用 ?new出一个对象

List<?> list = null;

Java中的可变参数

public static void add(String...arg)

在方法内可以把arg当成一个数组使用
可变参数可以是任意类型
可变参数的方法优先级比其他方法低,例如:
参数只有一个时,如果有两个方法,一个参数为单个String,一个参数为可变参数
虽然都可以匹配,但是一个参数的方法优先调用
如果有不止一个类型的参数,则可变参数最末尾匹配:
因为如果可变参数配置在前面,会引起程序混乱

public static void add(int i, String... arg)

JUnit基本测试

JUnit注解:

@Test注解可以提醒编译器这是个测试方法,并赋予一些功能
该注解无法测试静态方法,或有参数的方法

JUnit中常用的注解
@Before //在test注解方法前调用
@After //在test注解方法后调用

使用这两个方法可以把一些不必要测试的准备和善后工作分开,注重于测试本身

Java中的File

Java中File类的创建:
其中URL的地址可以是目录

File file = new File("d:test");

如果创建文件的话,要先将父目录取出判断是否创建,否则创建,再创建文件,否则Java会把文件名当成目录来创建

File类中的方法

isDirectory() :判断是否是一个目录(只有目录存在时才会判断正确)
isFile() :判断是否是一个文件
exists() :判断是否存在
canRead() :判断文件是否可读
canWrite() :判断文件是否可写
delete() :Java中为了防止误删,只有文件夹为空时,才可以删除文件夹,文件可以直接删除
renameTo(File dest) :修改文件或目录的名字
其实renameTo的操作可以看作时文件的移动,移动发生再同一个目录下,看起来就像是重命名一样,如果目录不一样,文件移动的效果就出来了,在移动的同时支持改名,用法和linux中的mv类似
getPath():获得文件的路径 + 名称(定义时的路径,如果定义文件时没有定义盘符,则默认在该项目目录下定义文件)
getAbsolutePath() :获得文件的绝对路径
getName():获取文件名
getParentFile():原理是对文件定义的字段进行切割
length():返回文件的长度(字节为单位)
lastModified():返回最后修改日期(毫秒)
list():列出子文件(字符串)
listFiles():以File对象的形式返回子文件

FilenameFilter类可以用作文件名称的过滤筛选,要重写accept方法

Java中的IO流

对文件和目录操作
Java中的IO,I表示input,O表示output,所以IO流也叫做输入输出流

把文件重硬盘读取到内存里面-》读取-》读入-》输入-》input-》输入流
从内存里面吧数据保存到硬盘-》存储-》写入-》输出-》output-》输出流

数据流

字节流 可以读取任意类型数据
抽象类型:InputStream OutPutStream
字符流 只可以读取文本数据
抽象类型:Reader、Writer

节点流(文件流FileReader、FileWriter)

处理流

流在使用完之后要关闭

FileInputStream

read():按照字节读取
read(byte[] b):将数据读取到b数组里

FileOutputStream

write():按照字节写入,参数为byte
write(byte[] b):将b数组的数据写入

缓冲区字节输出流
BufferedOutputStream

比普通的输出流多了一个缓冲区(字节数组),使用缓冲字节输出流,可以不用频繁地对硬盘写入,集中在缓冲区里一起写入,可以提高效率。
缓冲区数据只有在满地时候和字节流关闭的时候会自动写入,或者手动调用flush,写入硬盘。
缓冲区字节输入流:

BufferedInputStream

比普通的输入流对了一个缓冲区(字节数组),时用缓冲字节输出流,可以不用频繁的对硬盘读取,每次读取之前,缓冲中会自动读取一些数据到缓冲区中,读取时只要和缓冲区交互即可

转换流
字符输出流
OutputStreamWriter

将输出字节流转换为输出字符流,构造函数主要参数为字节流
在OutputStreamWriter中有一个缓冲区
OutputStreamWriter可以指定编码格式
字符输入流

InputStreamReader

将输入字节流转换为输入字符流,构造函数主要参数为字节流
InputStreamReader也可以指定编码格式

所有的字符流都是带有缓冲区的
字符流其实是字节流的一个封装

FileReader和FileWriter的功能和上述两个类的功能一样,不同点是构造时会方便一点

字符缓冲区流

虽然字符流自带有缓冲区但是不能够显示地设置缓冲区的大小,字符缓冲区流可以显式的设置缓冲区大小,别的功能一致

实现将基本数据类型的数据格式转化为字符串输出
打印流:PrintStream和PrintWriter
提供了一系列重载的print()和println()方法,用于多种数据类型的输出

打印流

PrintStream和PrintWriter的输出不会抛出IOException异常
PrintStream和PrintWriter有自动flush功能
PrintStream 打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况下,应该使用PrintWriter 类。
System.out返回的是PrintStream的实例

数据流

为了方便地操作Java语言的基本数据类型和String的数据,可以使用数据流。

数据流有两个类:(用于读取和写出基本数据类型、String类的数据)

DataInputStream和DataOutputStream
分别“套接”在InputStream和OutputStream子类的流上

对象流

对象流
  • ObjectInputStream和OjbectOutputSteam
  • 用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。
  • 序列化:用ObjectOutputStream类保存基本类型数据或对象的机制
  • 反序列化:用ObjectInputStream类读取基本类型数据或对象的机制
  • ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量
  • 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。//当其它程序获取了这种二进制流,就可以恢复成原来的Java对象
  • 序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输时可被还原
  • 序列化是RMI(Remote Method Invoke –远程方法调用)过程的参数和返回值都必须实现的机制,而RMI 是JavaEE的基础。因此序列化机制是JavaEE平台的基础
  • 如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一。否则,会抛出NotSerializableException异常

serialVersionUID

Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException),所以最好手动设置

随机存取文件流

这是一个可读可写的流,需要设置模式mode


类结构(部分)
字节流
	InputStream
		FileInputStream
		BufferedInputStream(间接子类)包装类
	OutputStream
		FileOutputStream
		BufferedOutputStream(间接子类)包装类
字符流
	Reader
		InputStreamReader
			FileReader
		BufferedReader
	Writer
		OutputStreamWriter
			FileWriter
		BufferedWriter
     ...

java中的枚举类型

枚举类型可以限定值域,增加代码的可读性
枚举类型在Java中以一个类的形式出现
枚举类是用来简化int常量的写法,同时增加安全性(例如不重复)
每个枚举类型的值默认都有一个对应的值,(从0开始递增)

public enum EnumTest {
	A,B,C,D
}

打印数值:

System.out.println(a.ordinal()); // 0
枚举遍历

values():返回枚举类型的数组,可以直接打印

java中类的加载机制

类的使用分成三个部分
类的加载、连接、初始化

程序运行时,系统将要用的java内加载到内存中,加载的时编译后的class文件,class文件包括了数据成员和方法成员,只有在被用到的时候才会加载

类加载器(JVM组件):
将类加载到内存中,并生成java.lang.Class对象
Bootstrap ClassLoader 根类加载器
加载JRE中的核心类

Extension ClassLoader 拓展类加载器
加载JRE中的拓展类
System ClassLoaser 系统类加载器
一般加载自己写的类
类的加载


Java中的反射

在Java中,一般只要用到了Class类,都是应用了反射技术
什么是反射?
在程序运行的时候,查看一个类有那些信息,包括数据成员和方法成员,这个过程称为反射。
怎么使用反射?
要是用反射一般需要获取到要反射的类的Class对象
任何对象都可以使用getClass方法,每个类只有一个Class对象,因为类加载到内存只会被加载一次
,不管有多少个实例
getClass():通过实力获取Class对象
直接赋值获取类对象(不常用):如

Class a = a.class

或者使用Class中的forName方法,但是要抛出异常

Class的反射一般用来将原本不会加载到内存中的类加载大内存中

Class中的方法

getConstructors:获取类的所有构造方法数组,为Constructor数组
可以使用Constructor的方法构造实例
newInstance():参数我Object类型的可变参数,但是构造出来的实例必须强制转换

获取单个构造器方法
getConstructor():参数为class对象例如:

//实例类A的构造器
public A(String s,int i,double d){

}

//获取该构造器
Class A = class.forName("A");
Constructor c = A.getConstructor(String.class,int.class,double.class);
A a = (A)c.newInstance("",1,1.0);

上述获取构造方法都是public的构造方法
可以获取所有类型的构造方法,包括私有
获取所有声明的构造器:
getDeclaredConstructors():作用同上
getDeclaredConstructor():作用同上

如果需要使用私有构造器,要先解除保护机制
setAccessible(true):解除java的保护机制,可以任意构造对象

同样,Class可以获取(get)字段(Field)和方法(Method),可以获取(get)公开的或是声明过的(Declared),且都可以精确获取和获取所有(s)。

getFields()
getMethod()
getDeclaredField()
getDeclaredFields()
getDeclaredMethods()

字段(Field类的方法)

获取到Field对象,需要获取值:
getInt():获取Int值
getString():获取String值
getDouble():获取double值

方法(Method类的方法)

方法的执行(需要对象):
invoke(对象引用,方法需要的参数):参数如果没有,可以不填,该方法执行返回Object对象作为容器承载方法的返回值,如果没有,则为null