注解、反射、集合
annotation
内置注解
override、deprecated(弃用)、SupressWarnings(镇压警告)
元注解 注解的注解
Target注解的作用是:描述注解的使用范围(即:被修饰的注解可以用在什么地方) 。
Reteniton注解的作用是:描述注解保留的时间范围(即:被描述的注解在它所修饰的类中可以被保留到何时)。
Documented注解的作用是:描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息。
Inherited注解的作用是:使被它修饰的注解具有继承性(如果某个类使用了被@Inherited修饰的注解,则其子类将自动具有该注解)。
自定义注解
public class test04 {
@MyAnnotation2(age = 12,name = "dudu")
public void test(){};
@MyAnnotation3("dudu")
public void test2(){};
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2 {
//注解的参数:参数类型+参数名();
String name() default "";
int age() default 0;
int id() default -1;
String[] schools() default {"清华","北大"};
}
//只有一个参数的时候,参数名字为value,则可以省略
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation3 {
String value();
}
接口回忆
普通类:只有具体实现
抽象类:具体实现和约束(抽象方法)都有
接口:只有约束 无法自己写方法,定义的所有方法都是抽象的public abstract的
接口可以多继承
实现了接口的类就必须重写接口中的方法
接口不能实例化
反射
得到class类的方式
5种
Person person = new Student();
System.out.println("这个人是" + person.name);
//通过类名获得
Class c1 = Student.class;
//通过对象获得
Class c2 = person.getClass();
//通过forname获得
Class c3 = Class.forName("org.example.reflection.Student");
//基本内置类型的包装类都有一个Type属性
Class c4 = Integer.TYPE;
//获得父类类型
Class c5 = c1.getSuperclass();
System.out.println(c5);
//Java 中 Class 对象的 toString() 默认输出 class 关键字 + 类的全限定名。
类加载内存分析
栈,堆,方法区(特殊的堆)
三步:加载、链接、初始化
类加载过程的第一步,主要完成下面 3 件事情:
通过全类名获取定义此类的二进制字节流。
将字节流所代表的静态存储结构转换为方法区的运行时数据结构。
在内存中生成一个代表该类的 java.lang.Class 对象,作为方法区这些数据的访问入口
类初始化
public static void main(String[] args) throws ClassNotFoundException {
//1.主动引用
//Son son = new Son();
//2.反射也会产生主动引用
//Class.forName("org.example.reflection.Son");
//System.out.println(Son.m);
//不会产生类的初始化的方式
//System.out.println(Son.b);
//Son[] array = new Son[10];
System.out.println(Son.M);
}
获得类的运行时结构
可以通过class获得的信息:
实现的全部接口、继承的父类、全部的构造器、全部的方法、全部的field、注解
动态创建对象执行方法
//获得class对象
Class c1 = Class.forName("org.example.reflection.user");
/*
//构造一个对象
user user = (user)c1.newInstance();//调用了无参构造器
System.out.println(user);
//通过构造器创建对象
Constructor c = c1.getDeclaredConstructor(String.class, int.class, int.class);
user user2 = (user)c.newInstance("dudu",03,23);
System.out.println(user2);*/
//通过反射调用普通方法
user u = (user) c1.newInstance();
Method setName = c1.getMethod("setName",String.class);//通过反射获取一个方法
setName.invoke(u,"kuang");//invoke表示激活,把对象也传进去了
System.out.println(u.getName());
//通过反射操作属性
user uu = (user) c1.newInstance();
Field name = c1.getDeclaredField("name");
name.setAccessible(true);//不能直接操作私有属性,需要取消安全检测
name.set(uu,"kuang2");
System.out.println(uu.getName());
获得注解信息
集合
动态保存多个对象;提供了一系列操作对象的方法;

框架体系
单列集合、双列集合 存放键值对
collection接口和常用方法
contains是否有某个元素,add,remove,isEmpty,size,clear
遍历方式
方式1:
迭代器遍历快捷键itit
next作用:指针下移+返回当前值
一次循环后希望再次遍历需要重置迭代器
方式2:
增强for循环,底层仍然是迭代器,快捷键I
方式3,普通for遍历
list接口方法
list接口是collection的子接口
常见三种实现类:ArrayList,LinkedList,Vector(遍历方式都是一样的三种,大概明白了点为什么要面向接口编程)
如果直接使用具体实现类,当修改具体类或想切换到其他实现时,可能需要大面积修改代码。
而使用接口,只需替换接口的实现类即可,其他代码无需修改。
元素存储有序、且可重复、支持索引


subList返回的是一个前闭后开的区间
add,addAll,remove,set,get,indexOf
ArrayList注意事项与源码
- 存放所有的元素,包括空值,甚至可以加入多个空值
- 是由数组来实现数据存储的
- 基本等同意vector,但ArrayList是线程不安全的,多线程的情况下不适合使用
![]()
transient关键字用来表示 瞬态(临时)变量,不会被序列化保存
对无参构造器的扩容进行分析 对有参构造器的扩容进行分析
jdk17源码改了,但是机制还是没变
图解源码
vector注意事项与源码
linkedList
继承了list和deque接口,维护了一个双向链表
javaguide源码分析
ArrayList改查效率高,LinkedList增删效率高,大多数业务以查询为主,用ArrayList的情况较多
set接口方法
- 无序(添加与取出数据的顺序不一致)
- 没有索引
- 不能重复,最多只包含一个null值
![]()
取出的顺序虽然和添加数据的顺序不一致,但是每次取出的顺序是一致的
遍历方式只有迭代器与增强for循环
hashSet

hashset还是set那些性质,底层是hashmap
//不能加入相同元素/数据
set.add("apple");
set.add("apple");//添加不了
set.add(new Dog("tom"));//是不同的对象
set.add(new Dog("tom"));//可以加入
//String类型??加入不了
set.add(new String("dudu"));
set.add(new String("dudu"));//加入不了
源码介绍
hashmap的底层是数组+链表+红黑树

public static void main(String[] args) {
HashSet set = new HashSet();
set.add("dudu");
/*执行add
public boolean add(E e) {
return map.put(e, PRESENT)==null;present是一个占位对象
}*/
/*执行put,该方法会执行hash(key)得到key对应的hash值(不是hashcode)
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
/*执行putVal
* final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;//定义辅助变量
//table是hashmap的一个数组,类型是一个node数组
if ((tab = table) == null || (n = tab.length) == 0)//table如果是空或者大小为0
n = (tab = resize()).length;//第一次扩容到16
*/
//根据key,得到hash,去计算key应该放到table表的哪个索引位置
//并把这个位置的对象赋给p
/* if ((p = tab[i = (n - 1) & hash]) == null)//等于是mod(n-1),数组的下标
tab[i] = newNode(hash, key, value, null);p为null,表示还没有存放元素,就创建一个Node
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))//哈希值相同且(key相同),判断的是第一个节点
e = p;//此时就不加入,赋给e
else if (p instanceof TreeNode)//节点为红黑树
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {//节点不是红黑树,是一个普通链表
for (int binCount = 0; ; ++binCount) {//循环遍历链表
if ((e = p.next) == null) {//在链表尾部加上节点
p.next = newNode(hash, key, value, null);
// 结点数量达到阈值(默认为 8 ),执行 treeifyBin 方法
// 这个方法会根据 HashMap 数组来决定是否转换为红黑树。
// 只有当数组长度大于或者等于 64 的情况下,才会执行转换红黑树操作,以减少搜索时间。否则,就是只是对数组扩容。
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))//如果有相同的节点,跳出循环
break;
p = e;//等于p向下移动一个
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)//实际大小超过阈值就进行扩容,这个实际大小是增加一个节点就算一个,
//哪怕数组没有占完
resize();
afterNodeInsertion(evict);
return null;
}
*/
System.out.println(set);
}
扩容也比较讲究,有个阈值,达到阈值就按照2倍进行扩容(加入的节点数量大于阈值,factor一般是0.75);
每个链表元素个数达到8之后,如果数组大小也达到了64就将链表进化成红黑树

LinkedHashSet
是hashset的子类,底层维护了一个数组+双向链表,链表维护了元素的次序

map接口
//Map与collection并列存在,保存的是键值对,hashset中的元素对应的是这里的键key,present对应的是这里的值value
//所以key不允许重复,可以为null值,但是最多只有一个
一对k-v放在一个hashMap$Node中
为了方便遍历创建了一个EntrySet集合,存放的元素类型Entry,而一个Entry对象就有k,v(这个好像就是用来方便遍历的?)
EntrySet其实是一个内部类,定义的元素类型是Map.Entry,实际上还是存放的hashMap$Node
static class Node<K,V> implements Map.Entry<K,V>
巴拉巴拉反正就是把hashMap$Node放到EntrySet中方便遍历啦,Map.Entry提供重要的方法
六种遍历方法

public class Map_ {
public static void main(String[] args) {
HashMap map = new HashMap();
map.put("no1","dudu");
map.put("no2","Amy");
map.put("no3","Bob");
//Map与collection并列存在,保存的是键值对,hashset中的元素对应的是这里的键key,present对应的是这里的值value
//所以key不允许重复,可以为null值,但是最多只有一个
//当有相同的k时就等价于替换
map.put("no1","dada");
//k-V之间是单向一对一关系
System.out.println(map);
//六种遍历方式
//第一组:key
Set keyset = map.keySet();//取出的keyset是一个set
//增强for
for (Object key : keyset) {
System.out.println(key +"-"+ map.get(key));
}
//迭代器
Iterator iterator = keyset.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next +"-"+ map.get(next));
}
System.out.println("================");
//第二组:values
//增强for
Collection values = map.values();//取出的value是一个collection
for (Object value : values) {
System.out.println(value);
}
//迭代器
Iterator iterator2 = values.iterator();
while (iterator2.hasNext()) {
Object next = iterator2.next();
System.out.println(next);
}
System.out.println("================");
//通过entrySet来获取
//增强for
Set enrtySet = map.entrySet();
for (Object entry : enrtySet) {
Map.Entry m = (Map.Entry) entry;//将entry转换为entrySet,向下转型(obj->Map.Entry)
//向下转型(从父类类型转为子类类型)是需要显式强制类型转换的,需要使用括号明确指定类型。向下转型是加了一部分,向上转型是抛弃一部分,向下转型比较危险
System.out.println(m.getKey()+"-"+m.getValue());
}
//迭代器
Iterator iterator3 = enrtySet.iterator();
while (iterator3.hasNext()) {
Object next = iterator3.next();
//向下转型
Map.Entry m = (Map.Entry) next;
System.out.println(m.getKey()+"-"+m.getValue());
}
}
}
map接口的常用方法
put添加,根据键删除remove,根据键取值get,获取元素个数size,isEmpty,clear,查找键是否存在containsKey
HashMap
HashMap没有实现同步,线程不安全
Map接口的常用类:HashMap、Hashtable、properties
底层还是数组+链表+红黑树
table数组-node节点-实现了Map.entry接口

源码分析同hashset
扩容触发机制:一个链表上的大于8但table数组小于64时进行扩容,如果达到64并且一个链表上的node数量大于8的话会进行树化;
扩容还是按照二倍进扩容,达到阈值就进行扩容(自己设计程序验证,增强源码阅读能力)
Hashtable & Properties
与Hashmap类似,Hashtable是线程安全的(synchronized),键与值都不能为空
底层的数组是Hashtable$Entry[],初始化大小为11,阈值为8,按照2倍+1的原则扩容
Properties 继承于 Hashtable,用于管理配置信息的类。
由于 Properties 继承自 Hashtable 类,因此具有 Hashtable 的所有功能,同时还提供了一些特殊的方法来读取和写入属性文件。
Properties 类常用于存储程序的配置信息,例如数据库连接信息、日志输出配置、应用程序设置等。使用Properties类,可以将这些信息存储在一个文本文件中,并在程序中读取这些信息
选择集合实现类
collections工具类
是一个用来操作set,list,map等集合的工具类
sort(比较器comparator的用法),shuffle(随机打乱),reverse,swap
max(list),max(list,new Comparator),min,frequency,copy,replaceAll
ex06
默认的hashcode是根据对象的内存地址来计算的
public class ex6
{
public static void main(String[] args) {
HashSet hashSet = new HashSet();
person p1 = new person("AA",1001);
person p2 = new person("BB",1002);
hashSet.add(p1);
hashSet.add(p2);
p1.name = "CC";
hashSet.remove(p1);//有两个输出,删除失败,删除的位置上没有node
System.out.println(hashSet);
hashSet.add(new person("CC",1001));
System.out.println(hashSet);//有三个输出,在新的位置上加入这个对象
hashSet.add(new person("AA",1001));
System.out.println(hashSet);//四个输出,在(AA,1001)后面加上这个对象
}
}
TreeSet,TreeMap,LinkedHashSet源码没看



浙公网安备 33010602011771号