这段时间学习很艰难,一直喜欢的女孩拒绝了我。以后不要天天学习没空撩妹了。
字符串,使用一对“” 引起来表示。
1.String声明为final的 ,不可被继承;
2.String实现了Serializable接口:表示字符串是支持序列化的。
实现了Comparable接口,表示String可以比较大小。
3.String 内部定义了 final char[] value用于存储字符串数据
4.String:代表不可变 的字符序列。简称不可变性。
体现:① 当对字符串重新赋值是,需要重写指定内存区域赋值,不能使用原有的value进行赋值;
② 当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值;
③ 当调用String的replace() 方法修改指定字符或字符串时,也需要重新指定内存区域,不能在原有的value上进行赋值;
5.通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。
6.字符串常量池中是不会存储相同内容的字符串的。
两个对象用 == , 比较的是地址值;
方式一:通过字面量定义的方式
方式二:通过new + 构造器的方式
//通过字面量定义的方式:此时s1和s2的数据javaEE声明在方法区中的字符串常量池中。
String s1 = "javaEE";
String s2= "javaEE";
//通过new+构造器的方式:此时s3和s4保存的地址值,是数据在堆空间中开辟空间以后的地址值;
String s3 = new String("javaEE");
String s4 = new String("javaEE");
System.out.println(s1==s2); //true
System.out.println(s1==s3); //false
System.out.println(s1==s4);//false
System.out.println(s3==s4);//false
面试题:
String s = new String(“abc”); 方式创建对象,在内存中创建了几个对象?
两个: 一个是堆空间中new 结构, 零一个是char[] 对应的常量池中的数据 :“abc”;
/*
结论:
1.常量与常量的拼接结果在常量池。且常量池不会存在相同内容的常量。
2.只要其中有一个是变量,结果就在堆中。
3.如果拼接的结果调用inter()方法,返回值就在常量池中。
*/
@Test
public void test3(){
String s1 ="javaEE";
String s2 ="hadoop";
String s3 ="javaEEhadoop";
String s4 ="javaEE"+"hadoop";
String s5 =s1+"hadoop";
String s6 ="javaEE"+s2;
String s7 =s1+s2;
System.out.println(s3 == s4); //true
System.out.println(s3 == s5); //false
System.out.println(s3 == s6);//false
System.out.println(s3 == s7);//false
System.out.println(s5 == s6);//false
System.out.println(s5 == s7);//false
System.out.println(s6 == s7);//false
String s8 = s5.intern(); /*返回值得到的s8使用的常量值已经存在的javaEEhadoop*/
System.out.println(s3==s8); //true
}
面试题:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LmrX3yse-1656435555535)(java基础笔记.assets/image-20220520134445358.png)]
9.1.3 StringBuffer 和StringBuilder
String、StringBuffer、StringBuilder三者的异同
String、StringBuffer、StringBuilder三者的异同
String:不可变的字符序列;底层使用char[]数组;
StringBuffer:可变的字符序列;线程安全,效率低;底层使用char[]数组;
StringBuilder:可变的字符序列;线程不安全,效率高;底层使用char[]数组;
源码分析
String str = new String(); //char[] value = new char[0];
String str1 = new String("abc"); //char[] value = new char[]{'a','b','c'};
StringBUffer sb1 = new StringBuffer(); //char[] value = new char[16];底层创建了一个长度是16的chhar型数组;
sb1.append('a');//value[0]='a';
sb1.append('b');//value[1]='b';
StringBuffer sb2 = new StringBuffer('abc');//char[] value = new char["abc".length+16];
//问题1:System.out.println(sb2.length()); //3
//问题2:扩容问题:如果要添加的数据底层数组盛不下了,那就需要扩容底层的数组。
//开发中建议使用:StringBuffer(int capacity), 这样尽量避免扩容次数;
常用方法总结:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EnPRVmK6-1656435555537)(java基础笔记.assets/image-20220521212641552.png)]
增:append(XXX)
删:delete(int start,int end)
改: setChat(int n,char ch) / replace(int start,int end,String str)
查: charAt(int n)
插:insert(int offset, xxx)
长度: length();
从高到低排列: StringBuilder > StringBuffer > String
返回当前时间与1970年1月1日0时0分0秒知寄件以毫秒为单位的时间差。
java.util.Date 类
java.sql.Date类 子类
/*
java.util.Date类
|---java.sql.Date类
1.两个构造器的使用
>构造器一:Date():创建一个对应当前时间的Date对象
>构造器二:创建指定毫秒数的Date对象
2.两个方法的使用
>toString():显示当前的年、月、日、时、分、秒
>getTime():获取当前Date对象对应的毫秒数。(时间戳)
3. java.sql.Date对应着数据库中的日期类型的变量
>如何实例化
>如何将java.util.Date对象转换为java.sql.Date对象
*/
@Test
public void test2(){
//构造器一:Date():创建一个对应当前时间的Date对象
Date date1 = new Date();
System.out.println(date1.toString());//Sat Feb 16 16:35:31 GMT+08:00 2019
System.out.println(date1.getTime());//1550306204104
//构造器二:创建指定毫秒数的Date对象
Date date2 = new Date(155030620410L);
System.out.println(date2.toString());
//创建java.sql.Date对象
java.sql.Date date3 = new java.sql.Date(35235325345L);
System.out.println(date3);//1971-02-13
//如何将java.util.Date对象转换为java.sql.Date对象
//情况一:
// Date date4 = new java.sql.Date(2343243242323L);
// java.sql.Date date5 = (java.sql.Date) date4;
//情况二:
Date date6 = new Date();
java.sql.Date date7 = new java.sql.Date(date6.getTime());
}
对日期Date类的格式化和解析
1.两个操作
格式化:日期 --> 字符串
解析:字符串 --> 日期
1.实例化
//1.实例化
//方式一:创建其子类(GregorianCalendar)的对象
//方式二:调用其静态方法getInstance()
Calendar calendar = Calendar.getInstance();
2.常用方法
//get()
//set()
//add()
//getImte() : 日历类 -->Date
Date date = calendar.getTime();
//setTime():Date ---> 日历类
Date date1 = new Date();
calendar.setTime(date1);
//获取本初子午线的时间
Instant instant = Instant.now();
//添加时间的偏移量
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));
//获取自1970年开始的毫秒数 --> Date类的getTime()
long milli = instant.toEpochMilli();
//通过给定的毫秒数,获取Instant实例 -->Date(long millis)
Instant instant1 = Instant.ofEpochMilli(1653208730975L);
格式化或解析日期、时间 , 类似于SimpleDateFormat
/*
DateTimeFormatter:格式化或解析日期、时间
类似于SimpleDateFormat
*/
@Test
public void test3(){
// 方式一:预定义的标准格式。如: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);//2019-02-18T15:42:18.797
//解析:字符串 -->日期
TemporalAccessor parse = formatter.parse("2019-02-18T15:42:18.797");
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);//2019年2月18日 下午03时47分16秒
// 本地化相关的格式。如: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);//2019-2-18
// 重点: 方式三:自定义的格式。如: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);//2019-02-18 03:52:09
//解析
TemporalAccessor accessor = formatter3.parse("2019-02-18 03:52:09");
System.out.println(accessor);
}
说明:Java中的对象,正常情况下,只能进行比较:== 或 != 。不能使用 > 或 < 的
但是在开发场景中,我们需要对多个对象进行排序,言外之意,就需要比较对象的大小。
如何实现?使用两个接口中的任何一个:Comparable 或 Comparator
Comparable接口与Comparator的使用的对比:
Comparable接口的方式一旦一定,保证Comparable接口实现类的对象在任何位置都可以比较大小。
Comparator接口属于临时性的比较。
1.Comparable接口的使用 (自然排序)
Comparable接口的使用举例: 自然排序
1.像String、包装类等实现了Comparable接口,重写了compareTo(obj)方法,给出了比较两个对象大小的方式。
2.像String、包装类重写compareTo()方法以后,进行了从小到大的排列
3. 重写compareTo(obj)的规则:
如果当前对象this大于形参对象obj,则返回正整数,
如果当前对象this小于形参对象obj,则返回负整数,
如果当前对象this等于形参对象obj,则返回零。
4. 对于自定义类来说,如果需要排序,我们可以让自定义类实现Comparable接口,重写compareTo(obj)方法。
在compareTo(obj)方法中指明如何排序
2.Comparator 接口 的使用 (定制排序)
Comparator接口的使用:定制排序
1.背景:
当元素的类型没有实现java.lang.Comparable接口而又不方便修改代码,
或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,
那么可以考虑使用 Comparator 的对象来排序
2.重写compare(Object o1,Object o2)方法,比较o1和o2的大小:
如果方法返回正整数,则表示o1大于o2;
如果返回0,表示相等;
返回负整数,表示o1小于o2。
如何自定义枚举类
如何使用关键字enum定义枚举类
Enum类的主要方法
实现接口的枚举类
枚举类的理解: 类的对象只有有限个,确定的。我们成此类为枚举类。
当需要定义一组常量时,强烈建议使用枚举类。
如果枚举类中只有一个对象, 则可以作为单例模式的实现方式。
声明类对象的属性。对象都是常量,所以属性用private final修饰
私有化类的构造器
对象赋值方式:显示赋值、构造器赋值、代码块赋值。如果此时显示赋值或代码块赋值则会导致类中对象的属性全部一样,所以采用构造器赋值;
提供当前枚举类的多个对象:public static final的
其他诉求:获取枚举类对象的属性
//自定义枚举类
class Season{
//1.声明Season对象的属性:private final修饰
private final String seasonName;
private final String seasonDesc;
//2.私有化类的构造器,并给对象属性赋值
private Season(String seasonName,String seasonDesc){
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
//3.提供当前枚举类的多个对象:public static final的
public static final Season SPRING = new Season("春天","春暖花开");
public static final Season SUMMER = new Season("夏天","夏日炎炎");
public static final Season AUTUMN = new Season("秋天","秋高气爽");
public static final Season WINTER = new Season("冬天","冰天雪地");
//4.其他诉求1:获取枚举类对象的属性
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
//4.其他诉求1:提供toString()
@Override
public String toString() {
return "Season{" +
"seasonName='" + seasonName + '\'' +
", seasonDesc='" + seasonDesc + '\'' +
'}';
}
}
//调用
public static void main(String[] args) {
Season spring = Season.SPRING;
System.out.println(spring);
}
说明:定义的枚举类默认继承于java.lang.Enum类
enum Season1 {
//1.提供当前枚举类的对象,多个对象之间用逗号隔开,末尾对象用分号结束
SPRING("春天","春暖花开"),
SUMMER("夏天","春暖花开"),
AUTUMN("秋天","春暖花开"),
WINTER("冬天","春暖花开");
//2.声明Season对象的属性:private final修饰
private final String seasonName;
private final String seasonDesc;
//2.私有化类的构造器,并给对象的属性赋值
Season1(String seasonName, String seasonDesc) {
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
//4.其他诉求1:获取枚举类对象的属性
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
}
values()方法:返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值。
valueOf(String str):可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常:IllegalArgumentException。
toString():返回当前枚举类对象常量的名称
interface Info{
void show();
}
//使用enum关键字枚举类
enum Season1 implements Info{
//1.提供当前枚举类的对象,多个对象之间用","隔开,末尾对象";"结束
SPRING("春天","春暖花开"){
@Override
public void show() {
System.out.println("春天在哪里?");
}
},
SUMMER("夏天","夏日炎炎"){
@Override
public void show() {
System.out.println("宁夏");
}
},
AUTUMN("秋天","秋高气爽"){
@Override
public void show() {
System.out.println("秋天不回来");
}
},
WINTER("冬天","冰天雪地"){
@Override
public void show() {
System.out.println("大约在冬季");
}
};
// ......
}
主要内容:
参照@suppressWarnings定义
如果注解有成员,在使用注解时,需要指明成员的值。
自定义注解必须配上注解的信息处理流程(使用反射)才有意义。
11.3 JDK中的元注解
用于修饰其他Annotation定义
元注解:对现有的注解进行解释说明的注解。
Retention:指定所修饰的 Annotation 的生命周期:SOURCE\CLASS(默认行为)\RUNTIME
只有声明为RUNTIME生命周期的注解,才能通过反射获取。
Target:用于指定被修饰的 Annotation 能用于修饰哪些程序元素
*******出现的频率较低*******
Documented:表示所修饰的注解在被javadoc解析时,保留下来。
Inherited:被它修饰的 Annotation 将具有继承性。
可重复注解:
① 在MyAnnotation上声明@Repeatable,成员值为MyAnnotations.class
② MyAnnotation的Target和Retention等元注解与MyAnnotations相同。
类型注解:
ElementType.TYPE_PARAMETER 表示该注解能写在类型变量的声明语句中(如:泛型声明)。
ElementType.TYPE_USE 表示该注解能写在使用类型的任何语句中。
* |----Collection接口:单列集合,用来存储一个一个的对象
* |----List接口:存储有序的、可重复的数据。 -->“动态”数组
* |----ArrayList、LinkedList、Vector
*
* |----Set接口:存储无序的、不可重复的数据 -->高中讲的“集合”
* |----HashSet、LinkedHashSet、TreeSet
*
* |----Map接口:双列集合,用来存储一对(key - value)一对的数据 -->高中函数:y = f(x)
* |----HashMap、LinkedHashMap、TreeMap、Hashtable、Properties
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cNwF2A2p-1656435555538)(java基础笔记.assets/image-20220528150638487.png)]
向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals().
add(Object obj),addAll(Collection coll),size(),isEmpty(),clear();
//下面的方法强制要求添加的元素所在类要重写equals()
contains(Object obj)//包含某个元素,
containsAll(Collection coll)//包含另外集合的所有元素,
remove(Object obj),
removeAll(Collection coll),
retainsAll(Collection coll) //获取当前集合和形参集合共有的元素,
equals(Object obj);
//********
hasCode() //获取hash值,
toArray()//集合转数组,
iterator();
//集合 --->数组:toArray()
Object[] arr = coll.toArray();
for(int i = 0;i < arr.length;i++){
System.out.println(arr[i]);
}
//拓展:数组 --->集合:调用Arrays类的静态方法asList(T ... t)
List<String> list = Arrays.asList(new String[]{"AA", "BB", "CC"});
System.out.println(list);
List arr1 = Arrays.asList(new int[]{123, 456});
System.out.println(arr1.size());//1
List arr2 = Arrays.asList(new Integer[]{123, 456});
System.out.println(arr2.size());//2
说明:
Iterator对象称为迭代器(设计模式的一种),主要用于遍历 Collection 集合中的元素。
GOF给迭代器模式的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。迭代器模式,就是为容器而生。
作用:遍历Collection 元素 , 不包含Map
如何获取实例
Iterator iterator = coll.iterator(); //返回一个迭代器实例
遍历的代码实现
Iterator iterator = coll.iterator();
//hasNext():判断是否还下一个元素
while(iterator.hasNext()){
//next():①指针下移 ②将下移以后集合位置上的元素返回
System.out.println(iterator.next());
}
图示说明[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-noZHmFNm-1656435555538)(java基础笔记.assets/image-20220528201529773.png)]
remove()的使用:
//测试Iterator中的remove()
//如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法,再调用remove都会报IllegalStateException。
//内部定义了remove(),可以在遍历的时候,删除集合中的元素。此方法不同于集合直接调用remove()
@Test
public void test3(){
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new Person("Jerry",20));
coll.add(new String("Tom"));
coll.add(false);
//删除集合中"Tom"
Iterator iterator = coll.iterator();
while (iterator.hasNext()){
// iterator.remove();
Object obj = iterator.next();
if("Tom".equals(obj)){
iterator.remove();
// iterator.remove();
}
}
//遍历集合
iterator = coll.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
存储的数据特点:存储序的、可重复的数据。
常用方法:(记住)
增:add(Object obj)
删:remove(int index) / remove(Object obj)
改:set(int index, Object ele)
查:get(int index)
插:add(int index, Object ele)
长度:size()
遍历:① Iterator迭代器方式
② 增强for循环
③ 普通的循环
常用实现类
|----Collection接口:单列集合,用来存储一个一个的对象
* |----List接口:存储序的、可重复的数据。 -->“动态”数组,替换原的数组
* |----ArrayList:作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[] elementData存储
* |----LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储
* |----Vector:作为List接口的古老实现类;线程安全的,效率低;底层使用Object[] elementData存储
源码分析(难点)
//ArrayList的源码分析:
* 1 jdk 7情况下
* ArrayList list = new ArrayList();//底层创建了长度是10的Object[]数组elementData
* list.add(123);//elementData[0] = new Integer(123);
* ...
* list.add(11);//如果此次的添加导致底层elementData数组容量不够,则扩容。
* 默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。
*
* 结论:建议开发中使用带参的构造器:ArrayList list = new ArrayList(int capacity)
*
* 2 jdk 8中ArrayList的变化:
* ArrayList list = new ArrayList();//底层Object[] elementData初始化为{}.并没创建长度为10的数组
*
* list.add(123);//第一次调用add()时,底层才创建了长度10的数组,并将数据123添加到elementData[0]
* ...
* 后续的添加和扩容操作与jdk 7 无异。
* 3 小结:jdk7中的ArrayList的对象的创建类似于单例的饿汉式,而jdk8中的ArrayList的对象
* 的创建类似于单例的懒汉式,延迟了数组的创建,节省内存。
//LinkedList的源码分析:
LinkedList list = new LinkedList(); 内部声明了Node类型的first和last属性,默认值为null
* list.add(123);//将123封装到Node中,创建了Node对象。
*
* 其中,Node定义为:体现了LinkedList的双向链表的说法
* private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
// Vector的源码分析
jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。
在扩容方面,默认扩容为原来的数组长度的2倍。
1. Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法。
2. 要求:向Set(主要指:HashSet、LinkedHashSet)中添加的数据,其所在的类一定要重写hashCode()和equals()
要求:重写的hashCode()和equals()尽可能保持一致性:相等的对象必须具有相等的散列码
重写两个方法的小技巧:对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值。
实现类: HashSet 、 LinkHashSet 、 TreeSet
一、存储无序、不可重复的 数据
以HashSet说明:
二、添加元素的过程,以HashSet说明
我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,
此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断
数组此位置上是否已经有元素:
如果此位置上没有其他元素,则元素a添加成功。 --->情况1
如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
如果hash值不相同,则元素a添加成功。--->情况2
如果hash值相同,进而需要调用元素a所在类的equals()方法:
equals()返回true,元素a添加失败
equals()返回false,则元素a添加成功。--->情况2
对于添加成功的情况2和情况3而言:元素a 与已经存在指定索引位置上数据以链表的方式存储。
jdk 7 :元素a放到数组中,指向原来的元素。
jdk 8 :原来的元素在数组中,指向元素a
总结:七上八下
HashSet底层:数组+链表的结构。
LinkHashSet
作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历
对于频繁的遍历操作,LinkedHashSet效率高于HashSet.
TreeSet
@Test
public void test2(){
Comparator com = new Comparator() {
//按照年龄从小到大排列
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof User && o2 instanceof User){
User u1 = (User)o1;
User u2 = (User)o2;
return Integer.compare(u1.getAge(),u2.getAge());
}else{
throw new RuntimeException("输入的数据类型不匹配");
}
}
};
TreeSet set = new TreeSet(com); //有参 用定制排序,无参时用User类里的自然排序
set.add(new User("Tom",12));
set.add(new User("Jerry",32));
set.add(new User("Jim",2));
set.add(new User("Mike",65));
set.add(new User("Mary",33));
set.add(new User("Jack",33));
set.add(new User("Jack",56));
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
集合Collection中存储的如果是自定义类的对象,需要自定义类重写哪个方法?为什么?
equals()方法。 contains() /remove()/retainsAll() ….
List:equals()方法
Set:(HashSet、LinkedHashSet为例):equals()、hashCode()
(TreeSet为例):Comparable:compareTo(Object obj)
Comparator:compare(Object o1,Object o2)
ArrayList,LinkedList,Vector三者的相同点与不同点
List 接口的常用方法有哪些?(增、删、改、查、插、长度、遍历)
add(Object obj)
remove(Object obj) //Collection 接口
remove(int index) //List接口自己的
set(int index,Object obj)
get(int index)
add(int index,Object obj)
size() //返回的是元素的个数,不是底层数组的长度
使用Iterator;foreach;普通的for
如何使用Iterator和增强for循环遍历List。举例说明
Set存储数据的特点是什么?常见的实现类有什么?说明一下彼此的特点。
无序不可重复
HashSet LinkedHashSet TreeSet
* |----Map:双列数据,存储key-value对的数据 ---类似于高中的函数:y = f(x)
* |----HashMap:作为Map的主要实现类;线程不安全的,效率高;存储null的key和value
* |----LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历。
* 原因:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。
* 对于频繁的遍历操作,此类执行效率高于HashMap。
* |----TreeMap:保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序
* 底层使用红黑树
* |----Hashtable:作为古老的实现类;线程安全的,效率低;不能存储null的key和value
* |----Properties:常用来处理配置文件。key和value都是String类型
*
*
* HashMap的底层:数组+链表 (jdk7及之前)
* 数组+链表+红黑树 (jdk 8)
Map中的key:无序的、不可重复的,使用Set存储所有的key ---> key所在的类要重写equals()和hashCode() (以HashMap为例)(如果是TreeMap就要重写排序方法了)
Map中的value:无序的、可重复的,使用Collection存储所有的value --->value所在的类要重写equals()
一个键值对:key-value构成了一个Entry对象。
Map中的entry:无序的、不可重复的,使用Set存储所有的entry
* 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倍,并将原有的数据复制过来。
*
* jdk8 相较于jdk7在底层实现方面的不同:
* 1. new HashMap():底层没有创建一个长度为16的数组
* 2. jdk 8底层的数组是:Node[],而非Entry[]
* 3. 首次调用put()方法时,底层创建长度为16的数组
* 4. jdk7底层结构只有:数组+链表。jdk8中底层结构:数组+链表+红黑树。
* 4.1 形成链表时,七上八下(jdk7:新的元素指向旧的元素。jdk8:旧的元素指向新的元素)
4.2 当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 > 64时,此时此索引位置上的所数据改为使用红黑树存储。
*
* DEFAULT_INITIAL_CAPACITY : HashMap的默认容量,16
* DEFAULT_LOAD_FACTOR:HashMap的默认加载因子:0.75
* threshold:扩容的临界值,=容量*填充因子:16 * 0.75 => 12
* TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树:8
* MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64
LinkedHashMap的底层实现原理(了解)
LinkedHashMap底层使用的结构与HashMap相同,因为LinkedHashMap继承于HashMap.
区别就在于:LinkedHashMap内部提供了Entry,替换HashMap中的Node.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mBknuSnA-1656435555539)(java基础笔记.assets/image-20220531000736696.png)]
添加、删除、修改操作:
Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
void putAll(Map m):将m中的所有key-value对存放到当前map中
Object remove(Object key):移除指定key的key-value对,并返回value
void clear():清空当前map中的所有数据
元素查询的操作:
Object get(Object key):获取指定key对应的value
boolean containsKey(Object key):是否包含指定的key
boolean containsValue(Object value):是否包含指定的value
int size():返回map中key-value对的个数
boolean isEmpty():判断当前map是否为空
boolean equals(Object obj):判断当前map和参数对象obj是否相等
元视图操作的方法:
Set keySet():返回所有key构成的Set集合
Collection values():返回所有value构成的Collection集合
Set entrySet():返回所有key-value对构成的Set集合
*总结:常用方法:
* 添加:put(Object key,Object value)
* 删除:remove(Object key)
* 修改:put(Object key,Object value)
* 查询:get(Object key)
* 长度:size()
* 遍历:keySet() / values() / entrySet()
@Test
public void test5(){
Map map = new HashMap();
map.put("AA",123);
map.put(45,1234);
map.put("BB",56);
//遍历所有的key集:keySet()
Set set = map.keySet();
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println();
//遍历所有的value集:values()
Collection values = map.values();
for(Object obj : values){
System.out.println(obj);
}
System.out.println();
//遍历所有的key-value
//方式一:entrySet()
Set entrySet = map.entrySet();
Iterator iterator1 = entrySet.iterator();
while (iterator1.hasNext()){
Object obj = iterator1.next();
//entrySet集合中的元素都是entry
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey() + "---->" + entry.getValue());
}
System.out.println();
//方式二:
Set keySet = map.keySet();
Iterator iterator2 = keySet.iterator();
while(iterator2.hasNext()){
Object key = iterator2.next();
Object value = map.get(key);
System.out.println(key + "=====" + value);
}
}
向TreeMap中添加key-value,要求key必须是由同一个类创建的对象,
因为要按照key进行排序:自然排序 、定制排序.
@Test
public void test1(){
TreeMap map = new TreeMap();
User u1 = new User("Tom",23);
User u2 = new User("Jerry",32);
User u3 = new User("Jack",20);
User u4 = new User("Rose",18);
map.put(u1,98);
map.put(u2,89);
map.put(u3,76);
map.put(u4,100);
Set entrySet = map.entrySet();
Iterator iterator1 = entrySet.iterator();
while (iterator1.hasNext()){
Object obj = iterator1.next();
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey() + "---->" + entry.getValue());
}
}
//定制排序
@Test
public void test2(){
TreeMap map = new TreeMap(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof User && o2 instanceof User){
User u1 = (User)o1;
User u2 = (User)o2;
return Integer.compare(u1.getAge(),u2.getAge());
}
throw new RuntimeException("输入的类型不匹配!");
}
});
User u1 = new User("Tom",23);
User u2 = new User("Jerry",32);
User u3 = new User("Jack",20);
User u4 = new User("Rose",18);
map.put(u1,98);
map.put(u2,89);
map.put(u3,76);
map.put(u4,100);
Set entrySet = map.entrySet();
Iterator iterator1 = entrySet.iterator();
while (iterator1.hasNext()){
Object obj = iterator1.next();
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey() + "---->" + entry.getValue());
}
}
Properties类是Hashtable的子类,key和value都是String类型的。常用来处理配置文件。
//Properties:常用来处理配置文件。key和value都是String类型
public static void main(String[] args) {
FileInputStream fis = null;
try {
Properties pros = new Properties();
fis = new FileInputStream("jdbc.properties");
pros.load(fis);//加载流对应的文件
String name = pros.getProperty("name");
String password = pros.getProperty("password");
System.out.println("name = " + name + ", password = " + password);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
作用 : 操作Collection 和Map的工具类。
reverse(List):反转 List 中元素的顺序
shuffle(List):对 List 集合元素进行随机排序
sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换
Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素
Object min(Collection)
Object min(Collection,Comparator)
int frequency(Collection,Object):返回指定集合中指定元素的出现次数
void copy(List dest,List src):将src中的内容复制到dest中
//报异常:IndexOutOfBoundsException("Source does not fit in dest")
// List dest = new ArrayList();
// Collections.copy(dest,list);
//正确的:
List dest = Arrays.asList(new Object[list.size()]);
System.out.println(dest.size());//list.size();
Collections.copy(dest,list);
boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换 List 对象的所有旧值
Collections 类中提供了多个 synchronizedXxx() 方法,
该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问。
synchronizedList(List list) 和 synchronizedMap(Map map)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sTBoZlP6-1656435555539)(java基础笔记.assets/image-20220531000853395.png)]
说明:ArrayList和HashMap都是线程不安全的,如果程序要求线程安全,我们可以将ArrayList、HashMap转换为线程的。
使用synchronizedList(List list) 和 synchronizedMap(Map map)
public class Order<T> {
String orderName;
int orderId;
//类的内部结构就可以使用类的泛型
T orderT;
public Order(){
//编译不通过
// T[] arr = new T[10];
//编译通过
T[] arr = (T[]) new Object[10];
}
public Order(String orderName,int orderId,T orderT){
this.orderName = orderName;
this.orderId = orderId;
this.orderT = orderT;
}
//如下的三个方法都不是泛型方法
public T getOrderT(){
return orderT;
}
public void setOrderT(T orderT){
this.orderT = orderT;
}
@Override
public String toString() {
return "Order{" +
"orderName='" + orderName + '\'' +
", orderId=" + orderId +
", orderT=" + orderT +
'}';
}
}
异常类不能声明为泛型。
静态方法中不能使用类的泛型。
//泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系。
//换句话说,泛型方法所属的类是不是泛型类都没有关系。
//泛型方法,可以声明为静态的。原因:泛型参数是在调用方法时确定的。并非在实例化类时确定。
public 【static】 <E> List<E> copyFromArrayToList(E[] arr){
ArrayList<E> list = new ArrayList<>();
for(E e : arr){
list.add(e);
}
return list;
}
虽然类A是类B的父类,但是G 和G二者不具备子父类关系,二者是并列关系。
补充:类A是类B的父类,A 是 B 的父类
通配符:?
File类的一个对象,代表一个文件或一个文件目录(俗称:文件夹)
相对路径
:相较于某个路径下,指明的路径。
绝对路径
:包含盘符在内的文件或文件目录的路径
File file1 = new File("hello.txt");//相对于当前module
File file2 = new File("D:\\workspace_idea1\\JavaSenior\\day08\\he.txt");
/*
public String getAbsolutePath():获取绝对路径
public String getPath() :获取路径
public String getName() :获取名称
public String getParent():获取上层文件目录路径。若无,返回null
public long length() :获取文件长度(即:字节数)。不能获取目录的长度。
public long lastModified() :获取最后一次的修改时间,毫秒值
如下的两个方法适用于文件目录:
public String[] list() :获取指定目录下的所有文件或者文件目录的名称数组
public File[] listFiles() :获取指定目录下的所有文件或者文件目录的File数组
*/
//public boolean renameTo(File dest):把文件重命名为指定的文件路径
// 比如:file1.renameTo(file2)为例:
// 要想保证返回true,需要file1在硬盘中是存在的,且file2不能在硬盘中存在。
File file1 = new File("hello.txt");
File file2 = new File("D:\\io\\hi.txt");
boolean renameTo = file2.renameTo(file1);
System.out.println(renameTo);
File类的判断功能
public boolean isDirectory():判断是否是文件目录 public boolean isFile() :判断是否是文件 public boolean exists() :判断是否存在 public boolean canRead() :判断是否可读 public boolean canWrite() :判断是否可写 public boolean isHidden() :判断是否隐藏
File类文件的创建/功能
创建硬盘中对应的文件或文件目录 public boolean createNewFile() :创建文件。若文件存在,则不创建,返回false public boolean mkdir() :创建文件目录。如果此文件目录存在,就不创建了。如果此文件目录的上层目录不存在,也不创建。 public boolean mkdirs() :创建文件目录。如果此文件目录存在,就不创建了。如果上层文件目录不存在,一并创建 删除磁盘中的文件或文件目录 public boolean delete():删除文件或者文件夹 删除注意事项:Java中的删除不走回收站。
@Test
public void test6() throws IOException {
File file1 = new File("hi.txt");
if(!file1.exists()){
//文件的创建
file1.createNewFile();
System.out.println("创建成功");
}else{//文件存在
file1.delete();
System.out.println("删除成功");
}
}
@Test
public void test7(){
//文件目录的创建
File file1 = new File("d:\\io\\io1\\io3");
boolean mkdir = file1.mkdir();
if(mkdir){
System.out.println("创建成功1");
}
File file2 = new File("d:\\io\\io1\\io4");
boolean mkdir1 = file2.mkdirs();
if(mkdir1){
System.out.println("创建成功2");
}
//要想删除成功,io4文件目录下不能有子目录或文件
File file3 = new File("D:\\io\\io1\\io4");
file3 = new File("D:\\io\\io1");
System.out.println(file3.delete());
}
* 1.操作数据单位:字节流、字符流 * 2.数据的流向:输入流、输出流 * 3.流的角色:节点流、处理流
流的体系结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aOk1V8pM-1656435555540)(java基础笔记.assets/image-20220603223200044.png)]
16.1 网络通信要素
16.1.1 ip和端口号
1.IP :唯一标识 Internet上的计算机
java中使用InetAddress类代表IP
端口号:标识计算机的进程(不同进程有不同的端口号)
端口号与ip地址的组合得出一个网络套接字:Socket。
16.1.2 网络通信协议
TCP:
@Test
public void server() {
ServerSocket ss =null;
Socket socket =null;
InputStream is =null;
ByteArrayOutputStream baos =null;
try{
//1.创建一个ServerSocket(指定端口号)
ss = new ServerSocket(8899);
socket = ss.accept(); //监听方法:建立连接后会生成客户端的Socket
is = socket.getInputStream();
baos = new ByteArrayOutputStream();
byte[] buffer = new byte[5];
int len;
while ((len = is.read(buffer))!=-1){
baos.write(buffer,0,len);
}
}catch (IOException e){
}finally {
if(ss!=null){
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket!=null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is!=null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (baos!=null) {
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
System.out.println(baos.toString());
}
@Test
public void client() {
Socket socket =null;
OutputStream os =null;
try {
//1.创建Socket对象,指明服务器地址和端口
socket = new Socket("127.0.0.1", 8899);
os = socket.getOutputStream();
os.write("你好,我是客户端".getBytes());
}catch (IOException e){
}finally {
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(os!=null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
UDP:
反射的特征: 动态性。
java反射机制提供的功能:
1.在运行时判断任意一个对象所属的类;
2.在运行时构造任意一个类的对象;
3.在运行时判断任意一个类所具有的成员变量和方法;
4.在运行时获取泛型信息;
5.在运行时调用任意一个对象的成员变量和方法;
6.在运行时处理注解;
7.生成动态代理
相关API;
java.lang.Class:反射的源头
java.lang.reflect.Method
java.lang.reflect.Field
java.lang.reflect.Construstor
…
类的加载过程:
程序经过javac.exe命令以后,会程程一个或多个字节码文件(.class结尾)。
接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程成为类的加载。加载到内存中的类,我们成为运行时类,次运行时类,就作为Class的一个实例。
换句话说,Class的实例就对应着一个运行时类。
加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。
1.class;
外部类,成员(尘缘内部类,静态内部类),局部内部类,匿名内部类
2.interface:接口
3.[] : 数组
4.enum: 枚举
5.annotation:注解@interface
6.primitive type : 基本数据类型
7.void
方式一:调用运行时类的属性:.class
Class clazz1 = Person.class;
System.out.println(clazz1);
方式二:通过运行时类的对象,调用getClass()
Person person = new Person();
Class clazz2 = person.getClass();
System.out.println(clazz2);
方式三:调用Class的静态方法:forName(String classPath)
Class clazz3 = Class.forName("Person");
System.out.println(clazz3);
方式四:使用类的加载器:ClassLoader
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class clazz4 = classLoader.loadClass("Person");
System.out.println(clazz4);
以上四种方式获取的clazz对象是同一个;
类的加载(Load) -> 类的链接(Link) -> 类的初始化(Initialize)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y0gtae8v-1656435555541)(java基础笔记.assets/image-20220611153351017.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SWNzgHBY-1656435555541)(java基础笔记.assets/image-20220611154351663.png)]
类加载器作用是用来把类(class)装载进内存的。JVM规范定义了如下类型的类的加载器。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6eTSn4Rx-1656435555542)(java基础笔记.assets/image-20220611194904980.png)]
Bootstap Classloader :引导类加载器:用C++编写的,是jvm自带的类加载器,负责java平台核心库,用来状态核心类库,该加载器无法直接获取。
Extension Classloader:扩展类加载器:负责jre/lib/ext目录下的jar包或 -D java.ext.dirs指定目录下的jar包装入工作库。
System Classloader:系统类加载器:负责java -classpath或 -D java.class.path指定的目录下的类与jar包装入工作,是最常用的加载器。
自定义类加载器:
读取配置文件的两种方式:
@Test
public void test2() throws IOException {
Properties pros = new Properties();
//默认在当前的module下
FileInputStream fis = new FileInputStream("jdbc.properties");
pros.load(fis);
String user = pros.getProperty("user");
String password = pros.getProperty("password");
System.out.println("user:"+user+",passwore:"+password);
}
@Test
public void test3() throws IOException {
Properties pros = new Properties();
//使用ClassLoader,配置文件默认识别为:当前module的src下
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("jdbc1.properties");
pros.load(is);
String user = pros.getProperty("user");
String password = pros.getProperty("password");
String address = pros.getProperty("address");
System.out.println("address:"+address+",passwore:"+password); }
Object obj = clazz.newInstance();//创建了对应的运行时类的对象
1.必须由空参的构造器;
2.权限修饰符的全校要足够,通常设置为public
@Test
public void test() throws IllegalAccessException, InstantiationException {
//通过反射创建对应的运行时类的对象,要求:
//1.运行时类必须提供空参的构造器
//2.空参的构造器的访问权限得够。通常设置为public
Class clazz = Person.class;
Object obj = clazz.newInstance();
System.out.println(obj);
}
@Test
public void test2() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class clazz2 = Person.class;
// Constructor constructor = clazz2.getConstructor(String.class, int.class);
// Object o = constructor.newInstance("zhangdan",20);
Constructor constructor = clazz2.getConstructor();
Object o = constructor.newInstance();
System.out.println(o);
}
第二段我不太清楚和第一段有啥区别
在javabean中要求提供一个public的空参构造器,原因:
1.便于通过反射,创建运行时类的对象。
2.便于自乐李继承此运行时类 时,默认调用super()时,保证父类有此构造器。
创建类对象的方式:
方式一:new +构造器
方式二:要创建Xxx类的对象,可以考虑:Xxx、Xxxs、XxxFactory、XxxBuilder类中查看是否有静态方法的存在。可以调用其静态方法,创建Xxx对象。
方式三:通过反射。
//getFields():获取当前运行时类及其父类中生命为public访问权限的属性
//getDeclaredFields():获取房钱运行时类中声明的所有属性。(不包含父类中声明的属性)
@Test
public void test(){
Class clazz = Person.class;
//获取属性结构
//getFields():获取当前运行时类及其父类中生命为public访问权限的属性
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("=====================");
//getDeclaredFields():获取房钱运行时类中声明的所有属性。(不包含父类中声明的属性)
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field : declaredFields) {
System.out.println(field);
}
}
//权限修饰符 数据类型 变量名
@Test
public void test2(){
Class clazz = Person.class;
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field : declaredFields) {
//1.权限修饰符
int modifier = field.getModifiers();
System.out.println(Modifier.toString(modifier));
//2.数据类型
Class<?> type = field.getType();
System.out.println(type.getName() + "\t");
//3.变量名
String name = field.getName();
System.out.println(name);
}
}
getAnnotations(): 获取方法声明的注解
getModifiers(): 获取权限修饰符
getReturnType(): 获取返回值类型
getReturnType():获取返回值类型
getName(): 获取方法名
getParameterTypes():获取形参列表
getExceptionTypes():抛出的异常
@Test
public void test2(){
Class clazz = Person.class;
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method method : declaredMethods) {
//1.获取方法声明的注解
Annotation[] annotations = method.getAnnotations();
for(Annotation annotation :annotations){
System.out.println(annotation);
}
//2.获取权限修饰符
int modifiers = method.getModifiers();
System.out.println(Modifier.toString(modifiers));
//3.获取返回值类型
Class<?> type = method.getReturnType();
System.out.println(type.getName() + "\t");
//4.获取方法名
System.out.println(method.getName()+"");
//5.获取形参列表
Class<?>[] parameterTypes = method.getParameterTypes();
if (!(parameterTypes==null&¶meterTypes.length == 0)) {
for(Class p :parameterTypes){
System.out.println(p.getName());
}
}
System.out.println("=====================");
//6.抛出的异常
Class<?>[] exceptionTypes = method.getExceptionTypes();
if(exceptionTypes.length>0){
System.out.println("throws");
for (Class e : exceptionTypes) {
System.out.println(e.getName());
}
}
}
}
17.6.3获取运行时类的其他结构
1.获取构造器
//getConstructors():获取当前运行时类中声明为public的构造器
//getDeclaredConstructors():获取当前运行时类中的所有构造器
Class<Person> clazz = Person.class;
//getConstructors():获取当前运行时类中声明为public的构造器
Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor< ? > constructor : constructors) {
System.out.println(constructor);
}
System.out.println("=======================");
//getDeclaredConstructors():获取当前运行时类中的所有构造器
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor< ? > constructor : declaredConstructors) {
System.out.println(constructor);
}
2.获取运行时类的父类
Class<Person> clazz = Person.class;
//获取运行时父类
Class<? super Person> superclass = clazz.getSuperclass();
System.out.println(superclass);
//获取运行时带泛型的父类
Type genericSuperclass = clazz.getGenericSuperclass();
System.out.println(genericSuperclass);
//获取运行时的带泛型的父类的泛型
Class clazz2 =Person.class;
Type genericSuperclass1 = clazz2.getGenericSuperclass();
ParameterizedType paramType = (ParameterizedType)genericSuperclass1;
//获取泛型类型
Type[] actualTypeArguments = paramType.getActualTypeArguments();
System.out.println(((Class) actualTypeArguments[0]).getName());
3.获取运行时类 实现的接口
Class clazz = Person.class;
//获取运行时类实现的接口
Class[] interfaces = clazz.getInterfaces();
for(Class c:interfaces){
System.out.println(c);
}
System.out.println("=============");
//获取运行时类的父类实现的接口
Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
for (Class c : interfaces1) {
System.out.println(c);
}
4.获取当前运行时类所在的包
Class<Person> clazz = Person.class;
Package pack = clazz.getPackage();
System.out.println(pack);
5.获取运行时类的注解
Class<Person> clazz = Person.class;
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
1.getField(): 只能获取public权限的 ,否则会抛异常
Class clazz = Person.class;
//创建运行时类的对象
Person p = (Person) clazz.newInstance();
//获取指定的属性
Field id = clazz.getField("id");
//设置当前属性的值; set():参数1:指明设置那个对象的属性; 参数2:将属性值设置多少
id.set(p,1001);
//获取当前属性的值; get():参数1:获取那个对象的当前属性值
int pId = (int) id.get(p);
System.out.println(pId);
2.getDeclaredField: 获取所有属性,需要设置属性访问权限
Class clazz = Person.class;
Person p = (Person) clazz.newInstance();//创建运行时类的对象
Field name = clazz.getDeclaredField("name");//获取运行时类中指定变量名的属性
name.setAccessible(true);
name.set(p,"tom");
System.out.println(name.get(p));
Class<Person> clazz = Person.class;
Person p = clazz.newInstance();
//获取指定的某个方法
//getDeclaredMethod(): 参数1:指明获取的方法的名称 参数2:指明获取的方法的形参列表
Method show = clazz.getDeclaredMethod("show", String.class);
show.setAccessible(true);
/*
invoke():参数1:方法调用者;参数2:给方法形参赋值的实参
invoke()的返回值纪委对应类中调用的方法的返回值。
*/
Object chn = show.invoke(p, "CHN");
System.out.println(chn);
System.out.println("+++++++++++++++++++++++++++++");
Method showDesc = clazz.getDeclaredMethod("showDesc");
showDesc.setAccessible(true);
Object returnVal= showDesc.invoke(Person.class);//如果调用的方法没有返回值,则invoke()返回null
// showDesc.invoke(null);//如果调用的是static方法,参数可以是null,因为static方法知道调用者是谁的
System.out.println(returnVal);
Class<Person> clazz = Person.class;
//getDeclaredConstructor():参数:指定构造器的参数列表
Constructor<Person> contructor = clazz.getDeclaredConstructor(String.class);
contructor.setAccessible(true);
//创建运行时类的对象
Person tom = contructor.newInstance("Tom");
System.out.println(tom);
这种方式不常用,像这种指定参数的构造器,不是每个类都通常有的,比如一个实体类有3个属性,而构造器形参只有一个,那么另外两个属性的赋值问题不能解决。
使用一个代理讲对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否一级何时将方法调用转到原始对象上。
之前为大家将结果代理机制的操作,属于静态代理,特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。最好可以通过一个代理类完成全部的代理功能。
动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。
动态代理使用的场合:
动态代理相比于静态代理的优点:
抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。
静态代理特点:代理类和被代理类在编译期间,就确定下来了。
静态代理结构:
提供一个接口 (每个代理类只能为一个接口服务),代理类和被代理类都实现此接口;
被代理类处理自己的方法;
代理类中,
声明一个属性,类型为被代理类的类型;
代理类中提供有参构造器,提供的参数为代理属性初始化;
构造器作用:代理类初始化时,用被代理类对象进行实例化
代理类的重写方法中,调用接口的方法,由于用被代理对象初始化,执行的是被代理对象的方法;
//提供一个接口(每一个代理类只能为一个接口服务)
//代理类和被代理类都实现这个接口
interface ClothFactory{
void produceCloth();
}
//被代理类
class NikeClothFactory implements ClothFactory {
@Override
public void produceCloth() {
System.out.println("Nike工厂生产一批运动服");
}
}
//代理类
class ProxyClothFactory implements ClothFactory{
//1.声明的一个属性,类型为接口的类型
private ClothFactory factory;
//2.提供代理类构造器,提供参数为代理类属性初始化
//这个构造器的作用:代理类初始化时,用被代理类对象进行实例化
public ProxyClothFactory(ClothFactory factory){
this.factory = factory;
}
@Override
public void produceCloth() {
System.out.println("代理工厂做一些准备工作");
factory.produceCloth(); //被代理类对象执行方法
System.out.println("代理工厂做一些后续的收尾工作");
}
}
public class StaticProxyTest {
public static void main(String[] args) {
//1.需要造代理类对象,由于代理类的构造器需要传被代理类的对象,所以首先造被代理类对象
NikeClothFactory nike = new NikeClothFactory();//被代理类对象
ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nike);//代理类对象
//2.通过代理类对象调用produceCloth()
proxyClothFactory.produceCloth();
}
}
interface Human{
String getBelife();
void eat(String food);
}
//被代理类
class SuperMan implements Human{
@Override
public String getBelife() {
return "I believe I can fly!";
}
@Override
public void eat(String food) {
System.out.println("我喜欢吃"+food);
}
}
/*
根据加载到内存中的被代理类到底是什么,动态的创建一个跟你实现接口一样的类,来代理对你的执行
想要实现动态代理,需要解决的问题?
一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象
二:当通过代理类的对象调用方法时,如何动态的去调用被代理类的同名方法。
*/
class ProxyFactory{
//调用此方法,返回一个代理类的对象。解决问题一
//这个方法根据传入的被代理类对象,返回一个该对象的代理类
public static Object getProxyInstance(Object obj){ //obj:被代理类的对象
MyInvocationHandler handler = new MyInvocationHandler();
handler.bind(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
}
}
class MyInvocationHandler implements InvocationHandler{
private Object obj; //需要使用被代理类的对象进行赋值
public void bind(Object obj){
this.obj = obj;
}
//当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke(),
//将被代理类要执行的方法a的功能声明在invoke()中
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
//obj:被代理类的对象
Object invoke = method.invoke(obj, args);
return invoke; //返回值
}
}
public class ProxyTest {
public static void main(String[] args) {
SuperMan superMan = new SuperMan();
Human instance = (Human) ProxyFactory.getProxyInstance(superMan); //代理类对象
instance.getBelife();//通过代理类对象调用方法时,会自动的调用被代理类中的同名的方法
instance.eat("咸梅干");
}
}
只有一个抽象方法的接口,称为函数式接口;
@FunctionalInterface
java 内置的4大核心函数式接口
消费型接口 Consumer void accept(T t)
攻击型接口 Supplier T get()
函数型接口 Function
断定型接口 Predicate boolean test(T t)
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用。
方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。
要求:实现接口的抽象方法的参数类别和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致。
方法引用使用情境:当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用。
方法引用本质上就是Lambda表达式,而Lambda表达式作为函数式接口的实例。所以方法引用,也是函数式接口的实例。
使用格式: 类(或对象):: 方法名。
具体分三种情况
对象::非静态方法
类::静态方法
类::非静态方法
方法引用的要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的形参列表和返回值类型形同。
构造器引用: 和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。
抽象方法的返回值类型即为构造器所属的类的类型。
数组引用:可以把数组看做是一个特殊的类,则写法与构造器引用一致。
Stream 自己不会存储元素。
Stream不会改变源对象,相反没他们会返回一个持有结果的新Stream。
Stream操作是延迟执行的 。这意味着他们会等到需要结果的时候才执行。
Stream的执行流程:
① Stream的实例化
② 一些列的中间操作(过滤、映射、…)
③ 终止操作
一个中间操作链,对数据源的数据进行处理;
一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用。
方式一:通过集合
Stream<Object> stream = list.stream(); //返回一个顺序流
Stream<Object> objectStream = list.parallelStream();//返回一个并行流
方拾贰:通过数组
Stream<Integer> stream1 = Arrays.stream(new Integer[]{3, 4, 5});
方式三:通过Stream的of()
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
方式四:创建无限流[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ALBlaGDi-1656435555543)(java基础笔记.assets/image-20220620214611115.png)]
filter(Predicate p) --接受Lambda,从流中排出某些元素。
limit(n)-- 截断流,使元素不超过给定数量。
skip(n)–跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个
distinct() --筛选,通过流所生成元素的hashCode() 和 equals()去除重复元素。
map(Function f) : 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap(Function f) :接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连成一个流。
sorted() : 产生一个新流,其中按自然顺序排序。
sorted(Comparator com): 产生一个新流,其中按比较器顺序排序
allMatch(Predicate p) : 检查是否匹配所有元素。
anyMatch(Predicate p ): 检查是否至少匹配一个元素。
noneMatch(Predicate p ): 检查是否没有匹配的元素。
findFirst : 返回第一个元素。
findAny : 返回当前流中的任意元素。
count : 返回流中元素的总个数。
max(Comparator c) : 返回流中最大值。
min(Comparator c) : 返回流中最小值。
forEach(Comsumer c): 内部迭代。
reduce(T identity, BinaryOperator ):可以将流中元素反复结合起来,得到一个值。返回一个T。
reduce(BinaryOperator) – 可以将流中元素反复结合起来,得到一个值。返回Optional。
collect(Collector c) – 将流转换为其他形式。接受一个Collector接口的实现,用于给Stream中元素做汇总的方法。
Collector接口中方法的实现决定了如何对流执行收集的操作(如收集到List、Set、Map)。
另外,Collectors实用类提供了很多静态方法,可以方便地创建常见收集器实例,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fKXRtKRR-1656435555544)(java基础笔记.assets/image-20220621235820161.png)]
作为函数式接口的实例。所以方法引用,也是函数式接口的实例。
使用格式: 类(或对象):: 方法名。
具体分三种情况
对象::非静态方法
类::静态方法
类::非静态方法
方法引用的要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的形参列表和返回值类型形同。
构造器引用: 和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。
抽象方法的返回值类型即为构造器所属的类的类型。
数组引用:可以把数组看做是一个特殊的类,则写法与构造器引用一致。
Stream 自己不会存储元素。
Stream不会改变源对象,相反没他们会返回一个持有结果的新Stream。
Stream操作是延迟执行的 。这意味着他们会等到需要结果的时候才执行。
Stream的执行流程:
① Stream的实例化
② 一些列的中间操作(过滤、映射、…)
③ 终止操作
一个中间操作链,对数据源的数据进行处理;
一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用。
方式一:通过集合
Stream<Object> stream = list.stream(); //返回一个顺序流
Stream<Object> objectStream = list.parallelStream();//返回一个并行流
方拾贰:通过数组
Stream<Integer> stream1 = Arrays.stream(new Integer[]{3, 4, 5});
方式三:通过Stream的of()
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
方式四:创建无限流[外链图片转存中…(img-ALBlaGDi-1656435555543)]
filter(Predicate p) --接受Lambda,从流中排出某些元素。
limit(n)-- 截断流,使元素不超过给定数量。
skip(n)–跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个
distinct() --筛选,通过流所生成元素的hashCode() 和 equals()去除重复元素。
map(Function f) : 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap(Function f) :接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连成一个流。
sorted() : 产生一个新流,其中按自然顺序排序。
sorted(Comparator com): 产生一个新流,其中按比较器顺序排序
allMatch(Predicate p) : 检查是否匹配所有元素。
anyMatch(Predicate p ): 检查是否至少匹配一个元素。
noneMatch(Predicate p ): 检查是否没有匹配的元素。
findFirst : 返回第一个元素。
findAny : 返回当前流中的任意元素。
count : 返回流中元素的总个数。
max(Comparator c) : 返回流中最大值。
min(Comparator c) : 返回流中最小值。
forEach(Comsumer c): 内部迭代。
reduce(T identity, BinaryOperator ):可以将流中元素反复结合起来,得到一个值。返回一个T。
reduce(BinaryOperator) – 可以将流中元素反复结合起来,得到一个值。返回Optional。
collect(Collector c) – 将流转换为其他形式。接受一个Collector接口的实现,用于给Stream中元素做汇总的方法。
Collector接口中方法的实现决定了如何对流执行收集的操作(如收集到List、Set、Map)。
另外,Collectors实用类提供了很多静态方法,可以方便地创建常见收集器实例,
[外链图片转存中…(img-fKXRtKRR-1656435555544)]
字符串,使用一对“” 引起来表示。
1.String声明为final的 ,不可被继承;
2.String实现了Serializable接口:表示字符串是支持序列化的。
实现了Comparable接口,表示String可以比较大小。
3.String 内部定义了 final char[] value用于存储字符串数据
4.String:代表不可变 的字符序列。简称不可变性。
体现:① 当对字符串重新赋值是,需要重写指定内存区域赋值,不能使用原有的value进行赋值;
② 当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值;
③ 当调用String的replace() 方法修改指定字符或字符串时,也需要重新指定内存区域,不能在原有的value上进行赋值;
5.通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。
6.字符串常量池中是不会存储相同内容的字符串的。
两个对象用 == , 比较的是地址值;
方式一:通过字面量定义的方式
方式二:通过new + 构造器的方式
//通过字面量定义的方式:此时s1和s2的数据javaEE声明在方法区中的字符串常量池中。
String s1 = "javaEE";
String s2= "javaEE";
//通过new+构造器的方式:此时s3和s4保存的地址值,是数据在堆空间中开辟空间以后的地址值;
String s3 = new String("javaEE");
String s4 = new String("javaEE");
System.out.println(s1==s2); //true
System.out.println(s1==s3); //false
System.out.println(s1==s4);//false
System.out.println(s3==s4);//false
面试题:
String s = new String(“abc”); 方式创建对象,在内存中创建了几个对象?
两个: 一个是堆空间中new 结构, 零一个是char[] 对应的常量池中的数据 :“abc”;
/*
结论:
1.常量与常量的拼接结果在常量池。且常量池不会存在相同内容的常量。
2.只要其中有一个是变量,结果就在堆中。
3.如果拼接的结果调用inter()方法,返回值就在常量池中。
*/
@Test
public void test3(){
String s1 ="javaEE";
String s2 ="hadoop";
String s3 ="javaEEhadoop";
String s4 ="javaEE"+"hadoop";
String s5 =s1+"hadoop";
String s6 ="javaEE"+s2;
String s7 =s1+s2;
System.out.println(s3 == s4); //true
System.out.println(s3 == s5); //false
System.out.println(s3 == s6);//false
System.out.println(s3 == s7);//false
System.out.println(s5 == s6);//false
System.out.println(s5 == s7);//false
System.out.println(s6 == s7);//false
String s8 = s5.intern(); /*返回值得到的s8使用的常量值已经存在的javaEEhadoop*/
System.out.println(s3==s8); //true
}
面试题:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GMvkKUpg-1656435615181)(java基础笔记.assets/image-20220520134445358.png)]
9.1.3 StringBuffer 和StringBuilder
String、StringBuffer、StringBuilder三者的异同
String、StringBuffer、StringBuilder三者的异同
String:不可变的字符序列;底层使用char[]数组;
StringBuffer:可变的字符序列;线程安全,效率低;底层使用char[]数组;
StringBuilder:可变的字符序列;线程不安全,效率高;底层使用char[]数组;
源码分析
String str = new String(); //char[] value = new char[0];
String str1 = new String("abc"); //char[] value = new char[]{'a','b','c'};
StringBUffer sb1 = new StringBuffer(); //char[] value = new char[16];底层创建了一个长度是16的chhar型数组;
sb1.append('a');//value[0]='a';
sb1.append('b');//value[1]='b';
StringBuffer sb2 = new StringBuffer('abc');//char[] value = new char["abc".length+16];
//问题1:System.out.println(sb2.length()); //3
//问题2:扩容问题:如果要添加的数据底层数组盛不下了,那就需要扩容底层的数组。
//开发中建议使用:StringBuffer(int capacity), 这样尽量避免扩容次数;
常用方法总结:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ar1lCZu3-1656435615184)(java基础笔记.assets/image-20220521212641552.png)]
增:append(XXX)
删:delete(int start,int end)
改: setChat(int n,char ch) / replace(int start,int end,String str)
查: charAt(int n)
插:insert(int offset, xxx)
长度: length();
从高到低排列: StringBuilder > StringBuffer > String
返回当前时间与1970年1月1日0时0分0秒知寄件以毫秒为单位的时间差。
java.util.Date 类
java.sql.Date类 子类
/*
java.util.Date类
|---java.sql.Date类
1.两个构造器的使用
>构造器一:Date():创建一个对应当前时间的Date对象
>构造器二:创建指定毫秒数的Date对象
2.两个方法的使用
>toString():显示当前的年、月、日、时、分、秒
>getTime():获取当前Date对象对应的毫秒数。(时间戳)
3. java.sql.Date对应着数据库中的日期类型的变量
>如何实例化
>如何将java.util.Date对象转换为java.sql.Date对象
*/
@Test
public void test2(){
//构造器一:Date():创建一个对应当前时间的Date对象
Date date1 = new Date();
System.out.println(date1.toString());//Sat Feb 16 16:35:31 GMT+08:00 2019
System.out.println(date1.getTime());//1550306204104
//构造器二:创建指定毫秒数的Date对象
Date date2 = new Date(155030620410L);
System.out.println(date2.toString());
//创建java.sql.Date对象
java.sql.Date date3 = new java.sql.Date(35235325345L);
System.out.println(date3);//1971-02-13
//如何将java.util.Date对象转换为java.sql.Date对象
//情况一:
// Date date4 = new java.sql.Date(2343243242323L);
// java.sql.Date date5 = (java.sql.Date) date4;
//情况二:
Date date6 = new Date();
java.sql.Date date7 = new java.sql.Date(date6.getTime());
}
对日期Date类的格式化和解析
1.两个操作
格式化:日期 --> 字符串
解析:字符串 --> 日期
1.实例化
//1.实例化
//方式一:创建其子类(GregorianCalendar)的对象
//方式二:调用其静态方法getInstance()
Calendar calendar = Calendar.getInstance();
2.常用方法
//get()
//set()
//add()
//getImte() : 日历类 -->Date
Date date = calendar.getTime();
//setTime():Date ---> 日历类
Date date1 = new Date();
calendar.setTime(date1);
//获取本初子午线的时间
Instant instant = Instant.now();
//添加时间的偏移量
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));
//获取自1970年开始的毫秒数 --> Date类的getTime()
long milli = instant.toEpochMilli();
//通过给定的毫秒数,获取Instant实例 -->Date(long millis)
Instant instant1 = Instant.ofEpochMilli(1653208730975L);
格式化或解析日期、时间 , 类似于SimpleDateFormat
/*
DateTimeFormatter:格式化或解析日期、时间
类似于SimpleDateFormat
*/
@Test
public void test3(){
// 方式一:预定义的标准格式。如: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);//2019-02-18T15:42:18.797
//解析:字符串 -->日期
TemporalAccessor parse = formatter.parse("2019-02-18T15:42:18.797");
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);//2019年2月18日 下午03时47分16秒
// 本地化相关的格式。如: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);//2019-2-18
// 重点: 方式三:自定义的格式。如: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);//2019-02-18 03:52:09
//解析
TemporalAccessor accessor = formatter3.parse("2019-02-18 03:52:09");
System.out.println(accessor);
}
说明:Java中的对象,正常情况下,只能进行比较:== 或 != 。不能使用 > 或 < 的
但是在开发场景中,我们需要对多个对象进行排序,言外之意,就需要比较对象的大小。
如何实现?使用两个接口中的任何一个:Comparable 或 Comparator
Comparable接口与Comparator的使用的对比:
Comparable接口的方式一旦一定,保证Comparable接口实现类的对象在任何位置都可以比较大小。
Comparator接口属于临时性的比较。
1.Comparable接口的使用 (自然排序)
Comparable接口的使用举例: 自然排序
1.像String、包装类等实现了Comparable接口,重写了compareTo(obj)方法,给出了比较两个对象大小的方式。
2.像String、包装类重写compareTo()方法以后,进行了从小到大的排列
3. 重写compareTo(obj)的规则:
如果当前对象this大于形参对象obj,则返回正整数,
如果当前对象this小于形参对象obj,则返回负整数,
如果当前对象this等于形参对象obj,则返回零。
4. 对于自定义类来说,如果需要排序,我们可以让自定义类实现Comparable接口,重写compareTo(obj)方法。
在compareTo(obj)方法中指明如何排序
2.Comparator 接口 的使用 (定制排序)
Comparator接口的使用:定制排序
1.背景:
当元素的类型没有实现java.lang.Comparable接口而又不方便修改代码,
或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,
那么可以考虑使用 Comparator 的对象来排序
2.重写compare(Object o1,Object o2)方法,比较o1和o2的大小:
如果方法返回正整数,则表示o1大于o2;
如果返回0,表示相等;
返回负整数,表示o1小于o2。
如何自定义枚举类
如何使用关键字enum定义枚举类
Enum类的主要方法
实现接口的枚举类
枚举类的理解: 类的对象只有有限个,确定的。我们成此类为枚举类。
当需要定义一组常量时,强烈建议使用枚举类。
如果枚举类中只有一个对象, 则可以作为单例模式的实现方式。
声明类对象的属性。对象都是常量,所以属性用private final修饰
私有化类的构造器
对象赋值方式:显示赋值、构造器赋值、代码块赋值。如果此时显示赋值或代码块赋值则会导致类中对象的属性全部一样,所以采用构造器赋值;
提供当前枚举类的多个对象:public static final的
其他诉求:获取枚举类对象的属性
//自定义枚举类
class Season{
//1.声明Season对象的属性:private final修饰
private final String seasonName;
private final String seasonDesc;
//2.私有化类的构造器,并给对象属性赋值
private Season(String seasonName,String seasonDesc){
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
//3.提供当前枚举类的多个对象:public static final的
public static final Season SPRING = new Season("春天","春暖花开");
public static final Season SUMMER = new Season("夏天","夏日炎炎");
public static final Season AUTUMN = new Season("秋天","秋高气爽");
public static final Season WINTER = new Season("冬天","冰天雪地");
//4.其他诉求1:获取枚举类对象的属性
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
//4.其他诉求1:提供toString()
@Override
public String toString() {
return "Season{" +
"seasonName='" + seasonName + '\'' +
", seasonDesc='" + seasonDesc + '\'' +
'}';
}
}
//调用
public static void main(String[] args) {
Season spring = Season.SPRING;
System.out.println(spring);
}
说明:定义的枚举类默认继承于java.lang.Enum类
enum Season1 {
//1.提供当前枚举类的对象,多个对象之间用逗号隔开,末尾对象用分号结束
SPRING("春天","春暖花开"),
SUMMER("夏天","春暖花开"),
AUTUMN("秋天","春暖花开"),
WINTER("冬天","春暖花开");
//2.声明Season对象的属性:private final修饰
private final String seasonName;
private final String seasonDesc;
//2.私有化类的构造器,并给对象的属性赋值
Season1(String seasonName, String seasonDesc) {
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
//4.其他诉求1:获取枚举类对象的属性
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
}
values()方法:返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值。
valueOf(String str):可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常:IllegalArgumentException。
toString():返回当前枚举类对象常量的名称
interface Info{
void show();
}
//使用enum关键字枚举类
enum Season1 implements Info{
//1.提供当前枚举类的对象,多个对象之间用","隔开,末尾对象";"结束
SPRING("春天","春暖花开"){
@Override
public void show() {
System.out.println("春天在哪里?");
}
},
SUMMER("夏天","夏日炎炎"){
@Override
public void show() {
System.out.println("宁夏");
}
},
AUTUMN("秋天","秋高气爽"){
@Override
public void show() {
System.out.println("秋天不回来");
}
},
WINTER("冬天","冰天雪地"){
@Override
public void show() {
System.out.println("大约在冬季");
}
};
// ......
}
主要内容:
参照@suppressWarnings定义
如果注解有成员,在使用注解时,需要指明成员的值。
自定义注解必须配上注解的信息处理流程(使用反射)才有意义。
11.3 JDK中的元注解
用于修饰其他Annotation定义
元注解:对现有的注解进行解释说明的注解。
Retention:指定所修饰的 Annotation 的生命周期:SOURCE\CLASS(默认行为)\RUNTIME
只有声明为RUNTIME生命周期的注解,才能通过反射获取。
Target:用于指定被修饰的 Annotation 能用于修饰哪些程序元素
*******出现的频率较低*******
Documented:表示所修饰的注解在被javadoc解析时,保留下来。
Inherited:被它修饰的 Annotation 将具有继承性。
可重复注解:
① 在MyAnnotation上声明@Repeatable,成员值为MyAnnotations.class
② MyAnnotation的Target和Retention等元注解与MyAnnotations相同。
类型注解:
ElementType.TYPE_PARAMETER 表示该注解能写在类型变量的声明语句中(如:泛型声明)。
ElementType.TYPE_USE 表示该注解能写在使用类型的任何语句中。
* |----Collection接口:单列集合,用来存储一个一个的对象
* |----List接口:存储有序的、可重复的数据。 -->“动态”数组
* |----ArrayList、LinkedList、Vector
*
* |----Set接口:存储无序的、不可重复的数据 -->高中讲的“集合”
* |----HashSet、LinkedHashSet、TreeSet
*
* |----Map接口:双列集合,用来存储一对(key - value)一对的数据 -->高中函数:y = f(x)
* |----HashMap、LinkedHashMap、TreeMap、Hashtable、Properties
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KpPD0Ite-1656435615185)(java基础笔记.assets/image-20220528150638487.png)]
向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals().
add(Object obj),addAll(Collection coll),size(),isEmpty(),clear();
//下面的方法强制要求添加的元素所在类要重写equals()
contains(Object obj)//包含某个元素,
containsAll(Collection coll)//包含另外集合的所有元素,
remove(Object obj),
removeAll(Collection coll),
retainsAll(Collection coll) //获取当前集合和形参集合共有的元素,
equals(Object obj);
//********
hasCode() //获取hash值,
toArray()//集合转数组,
iterator();
//集合 --->数组:toArray()
Object[] arr = coll.toArray();
for(int i = 0;i < arr.length;i++){
System.out.println(arr[i]);
}
//拓展:数组 --->集合:调用Arrays类的静态方法asList(T ... t)
List<String> list = Arrays.asList(new String[]{"AA", "BB", "CC"});
System.out.println(list);
List arr1 = Arrays.asList(new int[]{123, 456});
System.out.println(arr1.size());//1
List arr2 = Arrays.asList(new Integer[]{123, 456});
System.out.println(arr2.size());//2
说明:
Iterator对象称为迭代器(设计模式的一种),主要用于遍历 Collection 集合中的元素。
GOF给迭代器模式的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。迭代器模式,就是为容器而生。
作用:遍历Collection 元素 , 不包含Map
如何获取实例
Iterator iterator = coll.iterator(); //返回一个迭代器实例
遍历的代码实现
Iterator iterator = coll.iterator();
//hasNext():判断是否还下一个元素
while(iterator.hasNext()){
//next():①指针下移 ②将下移以后集合位置上的元素返回
System.out.println(iterator.next());
}
图示说明[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OZHvrRst-1656435615186)(java基础笔记.assets/image-20220528201529773.png)]
remove()的使用:
//测试Iterator中的remove()
//如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法,再调用remove都会报IllegalStateException。
//内部定义了remove(),可以在遍历的时候,删除集合中的元素。此方法不同于集合直接调用remove()
@Test
public void test3(){
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new Person("Jerry",20));
coll.add(new String("Tom"));
coll.add(false);
//删除集合中"Tom"
Iterator iterator = coll.iterator();
while (iterator.hasNext()){
// iterator.remove();
Object obj = iterator.next();
if("Tom".equals(obj)){
iterator.remove();
// iterator.remove();
}
}
//遍历集合
iterator = coll.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
存储的数据特点:存储序的、可重复的数据。
常用方法:(记住)
增:add(Object obj)
删:remove(int index) / remove(Object obj)
改:set(int index, Object ele)
查:get(int index)
插:add(int index, Object ele)
长度:size()
遍历:① Iterator迭代器方式
② 增强for循环
③ 普通的循环
常用实现类
|----Collection接口:单列集合,用来存储一个一个的对象
* |----List接口:存储序的、可重复的数据。 -->“动态”数组,替换原的数组
* |----ArrayList:作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[] elementData存储
* |----LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储
* |----Vector:作为List接口的古老实现类;线程安全的,效率低;底层使用Object[] elementData存储
源码分析(难点)
//ArrayList的源码分析:
* 1 jdk 7情况下
* ArrayList list = new ArrayList();//底层创建了长度是10的Object[]数组elementData
* list.add(123);//elementData[0] = new Integer(123);
* ...
* list.add(11);//如果此次的添加导致底层elementData数组容量不够,则扩容。
* 默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。
*
* 结论:建议开发中使用带参的构造器:ArrayList list = new ArrayList(int capacity)
*
* 2 jdk 8中ArrayList的变化:
* ArrayList list = new ArrayList();//底层Object[] elementData初始化为{}.并没创建长度为10的数组
*
* list.add(123);//第一次调用add()时,底层才创建了长度10的数组,并将数据123添加到elementData[0]
* ...
* 后续的添加和扩容操作与jdk 7 无异。
* 3 小结:jdk7中的ArrayList的对象的创建类似于单例的饿汉式,而jdk8中的ArrayList的对象
* 的创建类似于单例的懒汉式,延迟了数组的创建,节省内存。
//LinkedList的源码分析:
LinkedList list = new LinkedList(); 内部声明了Node类型的first和last属性,默认值为null
* list.add(123);//将123封装到Node中,创建了Node对象。
*
* 其中,Node定义为:体现了LinkedList的双向链表的说法
* private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
// Vector的源码分析
jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。
在扩容方面,默认扩容为原来的数组长度的2倍。
1. Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法。
2. 要求:向Set(主要指:HashSet、LinkedHashSet)中添加的数据,其所在的类一定要重写hashCode()和equals()
要求:重写的hashCode()和equals()尽可能保持一致性:相等的对象必须具有相等的散列码
重写两个方法的小技巧:对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值。
实现类: HashSet 、 LinkHashSet 、 TreeSet
一、存储无序、不可重复的 数据
以HashSet说明:
二、添加元素的过程,以HashSet说明
我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,
此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断
数组此位置上是否已经有元素:
如果此位置上没有其他元素,则元素a添加成功。 --->情况1
如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
如果hash值不相同,则元素a添加成功。--->情况2
如果hash值相同,进而需要调用元素a所在类的equals()方法:
equals()返回true,元素a添加失败
equals()返回false,则元素a添加成功。--->情况2
对于添加成功的情况2和情况3而言:元素a 与已经存在指定索引位置上数据以链表的方式存储。
jdk 7 :元素a放到数组中,指向原来的元素。
jdk 8 :原来的元素在数组中,指向元素a
总结:七上八下
HashSet底层:数组+链表的结构。
LinkHashSet
作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历
对于频繁的遍历操作,LinkedHashSet效率高于HashSet.
TreeSet
@Test
public void test2(){
Comparator com = new Comparator() {
//按照年龄从小到大排列
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof User && o2 instanceof User){
User u1 = (User)o1;
User u2 = (User)o2;
return Integer.compare(u1.getAge(),u2.getAge());
}else{
throw new RuntimeException("输入的数据类型不匹配");
}
}
};
TreeSet set = new TreeSet(com); //有参 用定制排序,无参时用User类里的自然排序
set.add(new User("Tom",12));
set.add(new User("Jerry",32));
set.add(new User("Jim",2));
set.add(new User("Mike",65));
set.add(new User("Mary",33));
set.add(new User("Jack",33));
set.add(new User("Jack",56));
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
集合Collection中存储的如果是自定义类的对象,需要自定义类重写哪个方法?为什么?
equals()方法。 contains() /remove()/retainsAll() ….
List:equals()方法
Set:(HashSet、LinkedHashSet为例):equals()、hashCode()
(TreeSet为例):Comparable:compareTo(Object obj)
Comparator:compare(Object o1,Object o2)
ArrayList,LinkedList,Vector三者的相同点与不同点
List 接口的常用方法有哪些?(增、删、改、查、插、长度、遍历)
add(Object obj)
remove(Object obj) //Collection 接口
remove(int index) //List接口自己的
set(int index,Object obj)
get(int index)
add(int index,Object obj)
size() //返回的是元素的个数,不是底层数组的长度
使用Iterator;foreach;普通的for
如何使用Iterator和增强for循环遍历List。举例说明
Set存储数据的特点是什么?常见的实现类有什么?说明一下彼此的特点。
无序不可重复
HashSet LinkedHashSet TreeSet
* |----Map:双列数据,存储key-value对的数据 ---类似于高中的函数:y = f(x)
* |----HashMap:作为Map的主要实现类;线程不安全的,效率高;存储null的key和value
* |----LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历。
* 原因:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。
* 对于频繁的遍历操作,此类执行效率高于HashMap。
* |----TreeMap:保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序
* 底层使用红黑树
* |----Hashtable:作为古老的实现类;线程安全的,效率低;不能存储null的key和value
* |----Properties:常用来处理配置文件。key和value都是String类型
*
*
* HashMap的底层:数组+链表 (jdk7及之前)
* 数组+链表+红黑树 (jdk 8)
Map中的key:无序的、不可重复的,使用Set存储所有的key ---> key所在的类要重写equals()和hashCode() (以HashMap为例)(如果是TreeMap就要重写排序方法了)
Map中的value:无序的、可重复的,使用Collection存储所有的value --->value所在的类要重写equals()
一个键值对:key-value构成了一个Entry对象。
Map中的entry:无序的、不可重复的,使用Set存储所有的entry
* 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倍,并将原有的数据复制过来。
*
* jdk8 相较于jdk7在底层实现方面的不同:
* 1. new HashMap():底层没有创建一个长度为16的数组
* 2. jdk 8底层的数组是:Node[],而非Entry[]
* 3. 首次调用put()方法时,底层创建长度为16的数组
* 4. jdk7底层结构只有:数组+链表。jdk8中底层结构:数组+链表+红黑树。
* 4.1 形成链表时,七上八下(jdk7:新的元素指向旧的元素。jdk8:旧的元素指向新的元素)
4.2 当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 > 64时,此时此索引位置上的所数据改为使用红黑树存储。
*
* DEFAULT_INITIAL_CAPACITY : HashMap的默认容量,16
* DEFAULT_LOAD_FACTOR:HashMap的默认加载因子:0.75
* threshold:扩容的临界值,=容量*填充因子:16 * 0.75 => 12
* TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树:8
* MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64
LinkedHashMap的底层实现原理(了解)
LinkedHashMap底层使用的结构与HashMap相同,因为LinkedHashMap继承于HashMap.
区别就在于:LinkedHashMap内部提供了Entry,替换HashMap中的Node.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FW7BYFV0-1656435615187)(java基础笔记.assets/image-20220531000736696.png)]
添加、删除、修改操作:
Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
void putAll(Map m):将m中的所有key-value对存放到当前map中
Object remove(Object key):移除指定key的key-value对,并返回value
void clear():清空当前map中的所有数据
元素查询的操作:
Object get(Object key):获取指定key对应的value
boolean containsKey(Object key):是否包含指定的key
boolean containsValue(Object value):是否包含指定的value
int size():返回map中key-value对的个数
boolean isEmpty():判断当前map是否为空
boolean equals(Object obj):判断当前map和参数对象obj是否相等
元视图操作的方法:
Set keySet():返回所有key构成的Set集合
Collection values():返回所有value构成的Collection集合
Set entrySet():返回所有key-value对构成的Set集合
*总结:常用方法:
* 添加:put(Object key,Object value)
* 删除:remove(Object key)
* 修改:put(Object key,Object value)
* 查询:get(Object key)
* 长度:size()
* 遍历:keySet() / values() / entrySet()
@Test
public void test5(){
Map map = new HashMap();
map.put("AA",123);
map.put(45,1234);
map.put("BB",56);
//遍历所有的key集:keySet()
Set set = map.keySet();
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println();
//遍历所有的value集:values()
Collection values = map.values();
for(Object obj : values){
System.out.println(obj);
}
System.out.println();
//遍历所有的key-value
//方式一:entrySet()
Set entrySet = map.entrySet();
Iterator iterator1 = entrySet.iterator();
while (iterator1.hasNext()){
Object obj = iterator1.next();
//entrySet集合中的元素都是entry
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey() + "---->" + entry.getValue());
}
System.out.println();
//方式二:
Set keySet = map.keySet();
Iterator iterator2 = keySet.iterator();
while(iterator2.hasNext()){
Object key = iterator2.next();
Object value = map.get(key);
System.out.println(key + "=====" + value);
}
}
向TreeMap中添加key-value,要求key必须是由同一个类创建的对象,
因为要按照key进行排序:自然排序 、定制排序.
@Test
public void test1(){
TreeMap map = new TreeMap();
User u1 = new User("Tom",23);
User u2 = new User("Jerry",32);
User u3 = new User("Jack",20);
User u4 = new User("Rose",18);
map.put(u1,98);
map.put(u2,89);
map.put(u3,76);
map.put(u4,100);
Set entrySet = map.entrySet();
Iterator iterator1 = entrySet.iterator();
while (iterator1.hasNext()){
Object obj = iterator1.next();
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey() + "---->" + entry.getValue());
}
}
//定制排序
@Test
public void test2(){
TreeMap map = new TreeMap(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof User && o2 instanceof User){
User u1 = (User)o1;
User u2 = (User)o2;
return Integer.compare(u1.getAge(),u2.getAge());
}
throw new RuntimeException("输入的类型不匹配!");
}
});
User u1 = new User("Tom",23);
User u2 = new User("Jerry",32);
User u3 = new User("Jack",20);
User u4 = new User("Rose",18);
map.put(u1,98);
map.put(u2,89);
map.put(u3,76);
map.put(u4,100);
Set entrySet = map.entrySet();
Iterator iterator1 = entrySet.iterator();
while (iterator1.hasNext()){
Object obj = iterator1.next();
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey() + "---->" + entry.getValue());
}
}
Properties类是Hashtable的子类,key和value都是String类型的。常用来处理配置文件。
//Properties:常用来处理配置文件。key和value都是String类型
public static void main(String[] args) {
FileInputStream fis = null;
try {
Properties pros = new Properties();
fis = new FileInputStream("jdbc.properties");
pros.load(fis);//加载流对应的文件
String name = pros.getProperty("name");
String password = pros.getProperty("password");
System.out.println("name = " + name + ", password = " + password);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
作用 : 操作Collection 和Map的工具类。
reverse(List):反转 List 中元素的顺序
shuffle(List):对 List 集合元素进行随机排序
sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换
Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素
Object min(Collection)
Object min(Collection,Comparator)
int frequency(Collection,Object):返回指定集合中指定元素的出现次数
void copy(List dest,List src):将src中的内容复制到dest中
//报异常:IndexOutOfBoundsException("Source does not fit in dest")
// List dest = new ArrayList();
// Collections.copy(dest,list);
//正确的:
List dest = Arrays.asList(new Object[list.size()]);
System.out.println(dest.size());//list.size();
Collections.copy(dest,list);
boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换 List 对象的所有旧值
Collections 类中提供了多个 synchronizedXxx() 方法,
该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问。
synchronizedList(List list) 和 synchronizedMap(Map map)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UoiDheEI-1656435615188)(java基础笔记.assets/image-20220531000853395.png)]
说明:ArrayList和HashMap都是线程不安全的,如果程序要求线程安全,我们可以将ArrayList、HashMap转换为线程的。
使用synchronizedList(List list) 和 synchronizedMap(Map map)
public class Order<T> {
String orderName;
int orderId;
//类的内部结构就可以使用类的泛型
T orderT;
public Order(){
//编译不通过
// T[] arr = new T[10];
//编译通过
T[] arr = (T[]) new Object[10];
}
public Order(String orderName,int orderId,T orderT){
this.orderName = orderName;
this.orderId = orderId;
this.orderT = orderT;
}
//如下的三个方法都不是泛型方法
public T getOrderT(){
return orderT;
}
public void setOrderT(T orderT){
this.orderT = orderT;
}
@Override
public String toString() {
return "Order{" +
"orderName='" + orderName + '\'' +
", orderId=" + orderId +
", orderT=" + orderT +
'}';
}
}
异常类不能声明为泛型。
静态方法中不能使用类的泛型。
//泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系。
//换句话说,泛型方法所属的类是不是泛型类都没有关系。
//泛型方法,可以声明为静态的。原因:泛型参数是在调用方法时确定的。并非在实例化类时确定。
public 【static】 <E> List<E> copyFromArrayToList(E[] arr){
ArrayList<E> list = new ArrayList<>();
for(E e : arr){
list.add(e);
}
return list;
}
虽然类A是类B的父类,但是G 和G二者不具备子父类关系,二者是并列关系。
补充:类A是类B的父类,A 是 B 的父类
通配符:?
File类的一个对象,代表一个文件或一个文件目录(俗称:文件夹)
相对路径
:相较于某个路径下,指明的路径。
绝对路径
:包含盘符在内的文件或文件目录的路径
File file1 = new File("hello.txt");//相对于当前module
File file2 = new File("D:\\workspace_idea1\\JavaSenior\\day08\\he.txt");
/*
public String getAbsolutePath():获取绝对路径
public String getPath() :获取路径
public String getName() :获取名称
public String getParent():获取上层文件目录路径。若无,返回null
public long length() :获取文件长度(即:字节数)。不能获取目录的长度。
public long lastModified() :获取最后一次的修改时间,毫秒值
如下的两个方法适用于文件目录:
public String[] list() :获取指定目录下的所有文件或者文件目录的名称数组
public File[] listFiles() :获取指定目录下的所有文件或者文件目录的File数组
*/
//public boolean renameTo(File dest):把文件重命名为指定的文件路径
// 比如:file1.renameTo(file2)为例:
// 要想保证返回true,需要file1在硬盘中是存在的,且file2不能在硬盘中存在。
File file1 = new File("hello.txt");
File file2 = new File("D:\\io\\hi.txt");
boolean renameTo = file2.renameTo(file1);
System.out.println(renameTo);
File类的判断功能
public boolean isDirectory():判断是否是文件目录 public boolean isFile() :判断是否是文件 public boolean exists() :判断是否存在 public boolean canRead() :判断是否可读 public boolean canWrite() :判断是否可写 public boolean isHidden() :判断是否隐藏
File类文件的创建/功能
创建硬盘中对应的文件或文件目录 public boolean createNewFile() :创建文件。若文件存在,则不创建,返回false public boolean mkdir() :创建文件目录。如果此文件目录存在,就不创建了。如果此文件目录的上层目录不存在,也不创建。 public boolean mkdirs() :创建文件目录。如果此文件目录存在,就不创建了。如果上层文件目录不存在,一并创建 删除磁盘中的文件或文件目录 public boolean delete():删除文件或者文件夹 删除注意事项:Java中的删除不走回收站。
@Test
public void test6() throws IOException {
File file1 = new File("hi.txt");
if(!file1.exists()){
//文件的创建
file1.createNewFile();
System.out.println("创建成功");
}else{//文件存在
file1.delete();
System.out.println("删除成功");
}
}
@Test
public void test7(){
//文件目录的创建
File file1 = new File("d:\\io\\io1\\io3");
boolean mkdir = file1.mkdir();
if(mkdir){
System.out.println("创建成功1");
}
File file2 = new File("d:\\io\\io1\\io4");
boolean mkdir1 = file2.mkdirs();
if(mkdir1){
System.out.println("创建成功2");
}
//要想删除成功,io4文件目录下不能有子目录或文件
File file3 = new File("D:\\io\\io1\\io4");
file3 = new File("D:\\io\\io1");
System.out.println(file3.delete());
}
* 1.操作数据单位:字节流、字符流 * 2.数据的流向:输入流、输出流 * 3.流的角色:节点流、处理流
流的体系结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3ci45xDI-1656435615188)(java基础笔记.assets/image-20220603223200044.png)]
16.1 网络通信要素
16.1.1 ip和端口号
1.IP :唯一标识 Internet上的计算机
java中使用InetAddress类代表IP
端口号:标识计算机的进程(不同进程有不同的端口号)
端口号与ip地址的组合得出一个网络套接字:Socket。
16.1.2 网络通信协议
TCP:
@Test
public void server() {
ServerSocket ss =null;
Socket socket =null;
InputStream is =null;
ByteArrayOutputStream baos =null;
try{
//1.创建一个ServerSocket(指定端口号)
ss = new ServerSocket(8899);
socket = ss.accept(); //监听方法:建立连接后会生成客户端的Socket
is = socket.getInputStream();
baos = new ByteArrayOutputStream();
byte[] buffer = new byte[5];
int len;
while ((len = is.read(buffer))!=-1){
baos.write(buffer,0,len);
}
}catch (IOException e){
}finally {
if(ss!=null){
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket!=null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is!=null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (baos!=null) {
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
System.out.println(baos.toString());
}
@Test
public void client() {
Socket socket =null;
OutputStream os =null;
try {
//1.创建Socket对象,指明服务器地址和端口
socket = new Socket("127.0.0.1", 8899);
os = socket.getOutputStream();
os.write("你好,我是客户端".getBytes());
}catch (IOException e){
}finally {
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(os!=null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
UDP:
反射的特征: 动态性。
java反射机制提供的功能:
1.在运行时判断任意一个对象所属的类;
2.在运行时构造任意一个类的对象;
3.在运行时判断任意一个类所具有的成员变量和方法;
4.在运行时获取泛型信息;
5.在运行时调用任意一个对象的成员变量和方法;
6.在运行时处理注解;
7.生成动态代理
相关API;
java.lang.Class:反射的源头
java.lang.reflect.Method
java.lang.reflect.Field
java.lang.reflect.Construstor
…
类的加载过程:
程序经过javac.exe命令以后,会程程一个或多个字节码文件(.class结尾)。
接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程成为类的加载。加载到内存中的类,我们成为运行时类,次运行时类,就作为Class的一个实例。
换句话说,Class的实例就对应着一个运行时类。
加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。
1.class;
外部类,成员(尘缘内部类,静态内部类),局部内部类,匿名内部类
2.interface:接口
3.[] : 数组
4.enum: 枚举
5.annotation:注解@interface
6.primitive type : 基本数据类型
7.void
方式一:调用运行时类的属性:.class
Class clazz1 = Person.class;
System.out.println(clazz1);
方式二:通过运行时类的对象,调用getClass()
Person person = new Person();
Class clazz2 = person.getClass();
System.out.println(clazz2);
方式三:调用Class的静态方法:forName(String classPath)
Class clazz3 = Class.forName("Person");
System.out.println(clazz3);
方式四:使用类的加载器:ClassLoader
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class clazz4 = classLoader.loadClass("Person");
System.out.println(clazz4);
以上四种方式获取的clazz对象是同一个;
类的加载(Load) -> 类的链接(Link) -> 类的初始化(Initialize)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ofotNo3V-1656435615189)(java基础笔记.assets/image-20220611153351017.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UO639Ge6-1656435615189)(java基础笔记.assets/image-20220611154351663.png)]
类加载器作用是用来把类(class)装载进内存的。JVM规范定义了如下类型的类的加载器。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9nAIS2bX-1656435615190)(java基础笔记.assets/image-20220611194904980.png)]
Bootstap Classloader :引导类加载器:用C++编写的,是jvm自带的类加载器,负责java平台核心库,用来状态核心类库,该加载器无法直接获取。
Extension Classloader:扩展类加载器:负责jre/lib/ext目录下的jar包或 -D java.ext.dirs指定目录下的jar包装入工作库。
System Classloader:系统类加载器:负责java -classpath或 -D java.class.path指定的目录下的类与jar包装入工作,是最常用的加载器。
自定义类加载器:
读取配置文件的两种方式:
@Test
public void test2() throws IOException {
Properties pros = new Properties();
//默认在当前的module下
FileInputStream fis = new FileInputStream("jdbc.properties");
pros.load(fis);
String user = pros.getProperty("user");
String password = pros.getProperty("password");
System.out.println("user:"+user+",passwore:"+password);
}
@Test
public void test3() throws IOException {
Properties pros = new Properties();
//使用ClassLoader,配置文件默认识别为:当前module的src下
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("jdbc1.properties");
pros.load(is);
String user = pros.getProperty("user");
String password = pros.getProperty("password");
String address = pros.getProperty("address");
System.out.println("address:"+address+",passwore:"+password); }
Object obj = clazz.newInstance();//创建了对应的运行时类的对象
1.必须由空参的构造器;
2.权限修饰符的全校要足够,通常设置为public
@Test
public void test() throws IllegalAccessException, InstantiationException {
//通过反射创建对应的运行时类的对象,要求:
//1.运行时类必须提供空参的构造器
//2.空参的构造器的访问权限得够。通常设置为public
Class clazz = Person.class;
Object obj = clazz.newInstance();
System.out.println(obj);
}
@Test
public void test2() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class clazz2 = Person.class;
// Constructor constructor = clazz2.getConstructor(String.class, int.class);
// Object o = constructor.newInstance("zhangdan",20);
Constructor constructor = clazz2.getConstructor();
Object o = constructor.newInstance();
System.out.println(o);
}
第二段我不太清楚和第一段有啥区别
在javabean中要求提供一个public的空参构造器,原因:
1.便于通过反射,创建运行时类的对象。
2.便于自乐李继承此运行时类 时,默认调用super()时,保证父类有此构造器。
创建类对象的方式:
方式一:new +构造器
方式二:要创建Xxx类的对象,可以考虑:Xxx、Xxxs、XxxFactory、XxxBuilder类中查看是否有静态方法的存在。可以调用其静态方法,创建Xxx对象。
方式三:通过反射。
//getFields():获取当前运行时类及其父类中生命为public访问权限的属性
//getDeclaredFields():获取房钱运行时类中声明的所有属性。(不包含父类中声明的属性)
@Test
public void test(){
Class clazz = Person.class;
//获取属性结构
//getFields():获取当前运行时类及其父类中生命为public访问权限的属性
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("=====================");
//getDeclaredFields():获取房钱运行时类中声明的所有属性。(不包含父类中声明的属性)
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field : declaredFields) {
System.out.println(field);
}
}
//权限修饰符 数据类型 变量名
@Test
public void test2(){
Class clazz = Person.class;
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field : declaredFields) {
//1.权限修饰符
int modifier = field.getModifiers();
System.out.println(Modifier.toString(modifier));
//2.数据类型
Class<?> type = field.getType();
System.out.println(type.getName() + "\t");
//3.变量名
String name = field.getName();
System.out.println(name);
}
}
getAnnotations(): 获取方法声明的注解
getModifiers(): 获取权限修饰符
getReturnType(): 获取返回值类型
getReturnType():获取返回值类型
getName(): 获取方法名
getParameterTypes():获取形参列表
getExceptionTypes():抛出的异常
@Test
public void test2(){
Class clazz = Person.class;
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method method : declaredMethods) {
//1.获取方法声明的注解
Annotation[] annotations = method.getAnnotations();
for(Annotation annotation :annotations){
System.out.println(annotation);
}
//2.获取权限修饰符
int modifiers = method.getModifiers();
System.out.println(Modifier.toString(modifiers));
//3.获取返回值类型
Class<?> type = method.getReturnType();
System.out.println(type.getName() + "\t");
//4.获取方法名
System.out.println(method.getName()+"");
//5.获取形参列表
Class<?>[] parameterTypes = method.getParameterTypes();
if (!(parameterTypes==null&¶meterTypes.length == 0)) {
for(Class p :parameterTypes){
System.out.println(p.getName());
}
}
System.out.println("=====================");
//6.抛出的异常
Class<?>[] exceptionTypes = method.getExceptionTypes();
if(exceptionTypes.length>0){
System.out.println("throws");
for (Class e : exceptionTypes) {
System.out.println(e.getName());
}
}
}
}
17.6.3获取运行时类的其他结构
1.获取构造器
//getConstructors():获取当前运行时类中声明为public的构造器
//getDeclaredConstructors():获取当前运行时类中的所有构造器
Class<Person> clazz = Person.class;
//getConstructors():获取当前运行时类中声明为public的构造器
Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor< ? > constructor : constructors) {
System.out.println(constructor);
}
System.out.println("=======================");
//getDeclaredConstructors():获取当前运行时类中的所有构造器
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor< ? > constructor : declaredConstructors) {
System.out.println(constructor);
}
2.获取运行时类的父类
Class<Person> clazz = Person.class;
//获取运行时父类
Class<? super Person> superclass = clazz.getSuperclass();
System.out.println(superclass);
//获取运行时带泛型的父类
Type genericSuperclass = clazz.getGenericSuperclass();
System.out.println(genericSuperclass);
//获取运行时的带泛型的父类的泛型
Class clazz2 =Person.class;
Type genericSuperclass1 = clazz2.getGenericSuperclass();
ParameterizedType paramType = (ParameterizedType)genericSuperclass1;
//获取泛型类型
Type[] actualTypeArguments = paramType.getActualTypeArguments();
System.out.println(((Class) actualTypeArguments[0]).getName());
3.获取运行时类 实现的接口
Class clazz = Person.class;
//获取运行时类实现的接口
Class[] interfaces = clazz.getInterfaces();
for(Class c:interfaces){
System.out.println(c);
}
System.out.println("=============");
//获取运行时类的父类实现的接口
Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
for (Class c : interfaces1) {
System.out.println(c);
}
4.获取当前运行时类所在的包
Class<Person> clazz = Person.class;
Package pack = clazz.getPackage();
System.out.println(pack);
5.获取运行时类的注解
Class<Person> clazz = Person.class;
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
1.getField(): 只能获取public权限的 ,否则会抛异常
Class clazz = Person.class;
//创建运行时类的对象
Person p = (Person) clazz.newInstance();
//获取指定的属性
Field id = clazz.getField("id");
//设置当前属性的值; set():参数1:指明设置那个对象的属性; 参数2:将属性值设置多少
id.set(p,1001);
//获取当前属性的值; get():参数1:获取那个对象的当前属性值
int pId = (int) id.get(p);
System.out.println(pId);
2.getDeclaredField: 获取所有属性,需要设置属性访问权限
Class clazz = Person.class;
Person p = (Person) clazz.newInstance();//创建运行时类的对象
Field name = clazz.getDeclaredField("name");//获取运行时类中指定变量名的属性
name.setAccessible(true);
name.set(p,"tom");
System.out.println(name.get(p));
Class<Person> clazz = Person.class;
Person p = clazz.newInstance();
//获取指定的某个方法
//getDeclaredMethod(): 参数1:指明获取的方法的名称 参数2:指明获取的方法的形参列表
Method show = clazz.getDeclaredMethod("show", String.class);
show.setAccessible(true);
/*
invoke():参数1:方法调用者;参数2:给方法形参赋值的实参
invoke()的返回值纪委对应类中调用的方法的返回值。
*/
Object chn = show.invoke(p, "CHN");
System.out.println(chn);
System.out.println("+++++++++++++++++++++++++++++");
Method showDesc = clazz.getDeclaredMethod("showDesc");
showDesc.setAccessible(true);
Object returnVal= showDesc.invoke(Person.class);//如果调用的方法没有返回值,则invoke()返回null
// showDesc.invoke(null);//如果调用的是static方法,参数可以是null,因为static方法知道调用者是谁的
System.out.println(returnVal);
Class<Person> clazz = Person.class;
//getDeclaredConstructor():参数:指定构造器的参数列表
Constructor<Person> contructor = clazz.getDeclaredConstructor(String.class);
contructor.setAccessible(true);
//创建运行时类的对象
Person tom = contructor.newInstance("Tom");
System.out.println(tom);
这种方式不常用,像这种指定参数的构造器,不是每个类都通常有的,比如一个实体类有3个属性,而构造器形参只有一个,那么另外两个属性的赋值问题不能解决。
使用一个代理讲对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否一级何时将方法调用转到原始对象上。
之前为大家将结果代理机制的操作,属于静态代理,特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。最好可以通过一个代理类完成全部的代理功能。
动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。
动态代理使用的场合:
动态代理相比于静态代理的优点:
抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。
静态代理特点:代理类和被代理类在编译期间,就确定下来了。
静态代理结构:
提供一个接口 (每个代理类只能为一个接口服务),代理类和被代理类都实现此接口;
被代理类处理自己的方法;
代理类中,
声明一个属性,类型为被代理类的类型;
代理类中提供有参构造器,提供的参数为代理属性初始化;
构造器作用:代理类初始化时,用被代理类对象进行实例化
代理类的重写方法中,调用接口的方法,由于用被代理对象初始化,执行的是被代理对象的方法;
//提供一个接口(每一个代理类只能为一个接口服务)
//代理类和被代理类都实现这个接口
interface ClothFactory{
void produceCloth();
}
//被代理类
class NikeClothFactory implements ClothFactory {
@Override
public void produceCloth() {
System.out.println("Nike工厂生产一批运动服");
}
}
//代理类
class ProxyClothFactory implements ClothFactory{
//1.声明的一个属性,类型为接口的类型
private ClothFactory factory;
//2.提供代理类构造器,提供参数为代理类属性初始化
//这个构造器的作用:代理类初始化时,用被代理类对象进行实例化
public ProxyClothFactory(ClothFactory factory){
this.factory = factory;
}
@Override
public void produceCloth() {
System.out.println("代理工厂做一些准备工作");
factory.produceCloth(); //被代理类对象执行方法
System.out.println("代理工厂做一些后续的收尾工作");
}
}
public class StaticProxyTest {
public static void main(String[] args) {
//1.需要造代理类对象,由于代理类的构造器需要传被代理类的对象,所以首先造被代理类对象
NikeClothFactory nike = new NikeClothFactory();//被代理类对象
ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nike);//代理类对象
//2.通过代理类对象调用produceCloth()
proxyClothFactory.produceCloth();
}
}
interface Human{
String getBelife();
void eat(String food);
}
//被代理类
class SuperMan implements Human{
@Override
public String getBelife() {
return "I believe I can fly!";
}
@Override
public void eat(String food) {
System.out.println("我喜欢吃"+food);
}
}
/*
根据加载到内存中的被代理类到底是什么,动态的创建一个跟你实现接口一样的类,来代理对你的执行
想要实现动态代理,需要解决的问题?
一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象
二:当通过代理类的对象调用方法时,如何动态的去调用被代理类的同名方法。
*/
class ProxyFactory{
//调用此方法,返回一个代理类的对象。解决问题一
//这个方法根据传入的被代理类对象,返回一个该对象的代理类
public static Object getProxyInstance(Object obj){ //obj:被代理类的对象
MyInvocationHandler handler = new MyInvocationHandler();
handler.bind(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
}
}
class MyInvocationHandler implements InvocationHandler{
private Object obj; //需要使用被代理类的对象进行赋值
public void bind(Object obj){
this.obj = obj;
}
//当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke(),
//将被代理类要执行的方法a的功能声明在invoke()中
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
//obj:被代理类的对象
Object invoke = method.invoke(obj, args);
return invoke; //返回值
}
}
public class ProxyTest {
public static void main(String[] args) {
SuperMan superMan = new SuperMan();
Human instance = (Human) ProxyFactory.getProxyInstance(superMan); //代理类对象
instance.getBelife();//通过代理类对象调用方法时,会自动的调用被代理类中的同名的方法
instance.eat("咸梅干");
}
}
只有一个抽象方法的接口,称为函数式接口;
@FunctionalInterface
java 内置的4大核心函数式接口
消费型接口 Consumer void accept(T t)
攻击型接口 Supplier T get()
函数型接口 Function
断定型接口 Predicate boolean test(T t)
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用。
方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。
要求:实现接口的抽象方法的参数类别和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致。
方法引用使用情境:当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用。
方法引用本质上就是Lambda表达式,而Lambda表达式作为函数式接口的实例。所以方法引用,也是函数式接口的实例。
使用格式: 类(或对象):: 方法名。
具体分三种情况
对象::非静态方法
类::静态方法
类::非静态方法
方法引用的要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的形参列表和返回值类型形同。
构造器引用: 和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。
抽象方法的返回值类型即为构造器所属的类的类型。
数组引用:可以把数组看做是一个特殊的类,则写法与构造器引用一致。
Stream 自己不会存储元素。
Stream不会改变源对象,相反没他们会返回一个持有结果的新Stream。
Stream操作是延迟执行的 。这意味着他们会等到需要结果的时候才执行。
Stream的执行流程:
① Stream的实例化
② 一些列的中间操作(过滤、映射、…)
③ 终止操作
一个中间操作链,对数据源的数据进行处理;
一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用。
方式一:通过集合
Stream<Object> stream = list.stream(); //返回一个顺序流
Stream<Object> objectStream = list.parallelStream();//返回一个并行流
方拾贰:通过数组
Stream<Integer> stream1 = Arrays.stream(new Integer[]{3, 4, 5});
方式三:通过Stream的of()
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
方式四:创建无限流[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ePKOB1Ia-1656435615191)(java基础笔记.assets/image-20220620214611115.png)]
filter(Predicate p) --接受Lambda,从流中排出某些元素。
limit(n)-- 截断流,使元素不超过给定数量。
skip(n)–跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个
distinct() --筛选,通过流所生成元素的hashCode() 和 equals()去除重复元素。
map(Function f) : 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap(Function f) :接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连成一个流。
sorted() : 产生一个新流,其中按自然顺序排序。
sorted(Comparator com): 产生一个新流,其中按比较器顺序排序
allMatch(Predicate p) : 检查是否匹配所有元素。
anyMatch(Predicate p ): 检查是否至少匹配一个元素。
noneMatch(Predicate p ): 检查是否没有匹配的元素。
findFirst : 返回第一个元素。
findAny : 返回当前流中的任意元素。
count : 返回流中元素的总个数。
max(Comparator c) : 返回流中最大值。
min(Comparator c) : 返回流中最小值。
forEach(Comsumer c): 内部迭代。
reduce(T identity, BinaryOperator ):可以将流中元素反复结合起来,得到一个值。返回一个T。
reduce(BinaryOperator) – 可以将流中元素反复结合起来,得到一个值。返回Optional。
collect(Collector c) – 将流转换为其他形式。接受一个Collector接口的实现,用于给Stream中元素做汇总的方法。
Collector接口中方法的实现决定了如何对流执行收集的操作(如收集到List、Set、Map)。
另外,Collectors实用类提供了很多静态方法,可以方便地创建常见收集器实例,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qvy27tLe-1656435615192)(java基础笔记.assets/image-20220621235820161.png)]