文章目录
集合
我们之前存储很多数据时,最先想到的就是数组,但数组初始化以后,长度就不可变了,不便于扩展。数组中提 供的属性和方法少,不便于进行添加、删除、插入等操作,且效率不高。同时无法直接获取存储元素的 个数。数组存储的数据是有序的、可以重复的。---->存储数据的特点单一;这时就要考虑Java 集合类,可以用于存储数量不等的多个对象,还可用于保存具有映射关系的关联数组。保存数据的时候,需要考虑使用集合。
Java 集合可分为 Collection 和 Map 两种体系。Collection接口:单列数据,定义了存取一组对象的方法 的集合List:元素有序、可重复的集合。Set:元素无序、不可重复的集合。Map接口:双列数据,保存 具有映射系“key-value对”的集合。
集合框架
集合主要是两组(单列集合,双列集合)
CoLLection 接口有两个重要的子接口List和Set ,他们的实现子类都是单列集合
Map接口的实现子类是双列集合,存放的K-V
1、Collection接口
既然Collection接口是集合中的顶层接口,那么它中定义的所有功能子类都可以使用。查阅API中描述的 Collection接口。Collection 层次结构 中的根接口。Collection 表示一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素,而另一些则不允许。一些 collection 是有序 的,而另一些则是无序的。这里我们不关心具体创建的Collection中的那个子类对象,这里重点演示的是 Collection接口中的方法。
里面可以保存Object
boolean add(E e):确保此 collection 包含指定的元素(可选操作)。
boolean addAll(Collection c):将指定 collection 中的所有元素都添加到 此collection 中(可选操作)。
void clear():移除此 collection 中的所有元素(可选操作)。
boolean contains(Object o):如果此 collection 包含指定的元素,则返回 true 。
boolean containsAll(Collection c):如果此 collection 包含指定 collection 中的 所有元素,则返回 true 。
boolean equals(Object o):比较此 collection 与指定对象是否相等。
int hashCode():返回此 collection 的哈希码值。
boolean isEmpty():如果此 collection 不包含元素,则返回 true 。
Iterator iterator():返回在此 collection 的元素上进行迭代的迭代器。
boolean remove(Object o):从此 collection 中移除指定元素的单个实例,如果存在的话(可 选操作)。 如果传进一个0的话,是看成值而不是下标
boolean removeAll(Collection c):移除此 collection 中那些也包含在指定 collection 中的所有元素(可选操作)。
boolean retainAll(Collection c):仅保留此 collection 中那些也包含在指定 collection 的元素(可选操作)。
int size():返回此 collection 中的元素数。
Object[] toArray():返回包含此 collection 中所有元素的数组。
T[] toArray(T[] a):返回包含此 collection 中所有元素的数组;返回数组的运行时类型与指定 数组的运行时类型相同
Iterator:迭代器,集合很多,但是每种集合有自己保存数据的数据结构,使用迭代器去除集合中的元素
先判断集合中是否有元素,如果有,取出
hasNext:判断是否有下一个元素
next:获取下一个元素
remove:移除元素
●Collection接口实现类的特点
public interface Collection extends Iterable
- collection实现子类可以存放多个元素,每个元素可以是Object
2)有些Collection的实现类,可以存放重复的元素,有些不可以
3)有些Collection的实现类,有些是有序的(List), 有些不是有序(Set) - Collection接口没有直接的实现子类,是通过它的子接口Set和List来
实现的
Iterortor
List接口
List接口时collection接口下的一个接口
鉴于Java中数组用来存储数据的局限性,我们通常使用List替代数组。一个有序集合(也被称为序列)。 此接口的用户在列表中的每个元素都被插入的地方有精确的控制。用户可以通过它们的整数索引(在列 表中的位置)访问元素,并在列表中搜索元素。 与set不同的是,列表通常允许重复元素。
List集合类中元素有序、且可重复,集合中的每个元素都有其对应的顺序索引。
List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
JDK API中List接口的实现类常用的有:ArrayList、LinkedList和Vector。
常用方法
void add(int index, E element) :在列表的指定位置插入指定元素(可选操作)。
boolean addAll(int index, Collection c) :将指定 collection 中的 所有元素都插入到列表中的指定位置。
E get(int index) :返回列表中指定位置的元素。
int indexOf(Object o) :返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元 素,则返回 -1。
int lastIndexOf(Object o) :返回此列表中最后出现的指定元素的索引;如果列表不包含此元 素,则返回 -1。
ListIterator listIterator() :返回此列表元素的列表迭代器(按适当顺序)。 E remove(int index) :移除列表中指定位置的元素(可选操作)。
E set(int index, E element) :用指定元素替换列表中指定位置的元素(可选操作)。
List subList(int fromIndex, int toIndex) :返回列表中指定的 fromIndex (包括 )和 toIndex (不包括)之间的部分视图。
default void sort(Comparator c) :分类列表使用提供的 Comparator比较元 素。
List 实现子类
Collection接口:单列集合,用来存储一个一个的对象。
List接口:存储有序的、可重复的数据。 -->“动态”数组,替换原有的数组。
ArrayList:底层是数组,7是再创建集合的时候,10个长度的Object类型的数组就被创建了,后面扩容是原来长度的一半.循环遍历比较高
LinkedList:链表结构 Node. 插入和删除效率比较高
Vector:底层也是数组 ,古老的类,线程安全的,效率比较低 ,不常用 , 扩容是原来长度的一倍.
即使需要使用一个 线程安全的list,也会将ArrayList做一个转换Collections.synchronizedList(list); 此时得到的集合就是一个线程安全的集合。
主要看这三个类的底层代码
ArrayList底层
构造函数:
public ArrayList(int initialCapacity):根据这个数字创建一个长度数组,若小于0会报错
public ArrayList():生成默认空数组
public ArrayList(Collection<? extends E> c):会先看是不是同一个元素再依次复制
public void trimToSize():重置大小,因为每次添加如果需要扩容的话,就是扩容1.5倍,存在空间浪费,该方法就是去掉为null的元素空间
枚举与注解
枚举
引出:
和我们一致使用的类差不多,但平常使用的类,我们课以随便创建对象,这里的枚举就有限制,比如一个季节类,如果也可以随便创建的话,那么春天、夏天、红天、绿天…很显然不合理。
一般用两种实现方法:
自定义类
枚举类
自定义枚举类,利用类实现
package 枚举和注解.枚举;
//自定义实现枚举
public class Demo1 {
public static void main(String[] args) {
System.out.println(Season.SPRING);
}
}
//自定义枚举
class Season{
private String name;
private String desc;
//1、构造私有化
private Season(String name, String desc) {
this.name = name;
this.desc = desc;
}
//2、无set方法,因为季节不可改
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
//3、直接在内部创建固定对象
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、重写toString
@Override
public String toString() {
return "Season{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}
枚举类
package 枚举和注解.枚举;
public class Demo2 {
public static void main(String[] args) {
System.out.println(Season2.AUTUMN);
WeekDays weekDays1 = WeekDays.FRI;
WeekDays weekDays2 = WeekDays.FRI;
System.out.println(weekDays1 == weekDays2);//true
}
}
//枚举类
enum Season2{
//注意三点,和平常类的区别
//1、class改成enum
//2、对象创建有区别,用,间隔
//3、对象创建在最上面
SPRING("春天", "温暖"),
SUMMER("夏天", "炎热"),
AUTUMN("秋天", "凉爽"),
WINTER("冬天", "寒冷");
private String name;
private String desc;
private Season2(String name, String desc) {
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
@Override
public String toString() {
return "Season2{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}
//还是上面的枚举类,如果对象还有接口需要实现呢
enum Season3 implements info{
//注意三点,和平常类的区别
//1、class改成enum
//2、对象创建有区别,用,间隔
//3、对象创建在最上面
SPRING("春天", "温暖"){
public void show(){
System.out.println("");
}
},
SUMMER("夏天", "炎热"){
public void show(){
System.out.println("");
}
},
AUTUMN("秋天", "凉爽"){
public void show(){
System.out.println("");
}
},
WINTER("冬天", "寒冷"){
public void show(){
System.out.println("");
}
};
//可以每个对象都重写一下接口方法,当然也可以直接枚举类中实现
private String name;
private String desc;
private Season3(String name, String desc) {
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
@Override
public String toString() {
return "Season2{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}
interface info{
public void show();
}
//如果将上面的枚举再简化一点呢
enum WeekDays {
MON,TUE,WED,THR,FRI,STA,SUN;
//这个就相当于,创建了对象,这些对象全部调用无参构造方法;
//这样使用一般都是把里面内容当成自定义常量来使用
}
注解
1)注解(Annotation)也被称为元数据(Metadata),用于修饰解释包、类、方法、属性、构造器、局部变量等数据信息。
2)和注释一 样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息。
3)在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替java EE旧版中所遗留的繁冗代码和XML配置等。
使用Annotation时要在其前面增加@符号,并把该Annotation当成一个修饰符使用。用于修饰它支持的程序元素
三个基本的Annotation:
- @Override:限定某个方法,是重写父类方法,该注解只能用于方法
- @Deprecated:用于表示某个程序元素(类,方法等)已过时
- @SuppressWarnings:抑制编译器警告
注:@interface不是interface,是注解类是jdk5之后加入的
@Override
这个就不举例了,我们经常遇到
1、表示重写父类方法、不一定是直接父类
2、不写@Override的话,重写方法也不会影响
3、那么@Override作用是什么呢?@Override表示接下来要重写方法,防止本来打算重写的方法写错,
比如父类中存在say方法,你本来打算重写该方法,但不小心写错了写成了see方法,如果不加@Override,那么就会把see方法当成自己的方法,不看成是重写,但如果加上@Override的话,就会报错,说明要重写的方法,并不存在。
@Deprecated
@Deprecated:用于表示某个程序元素(类,方法等)已过时
package 枚举和注解.注解;
public class Demo2 {
A a = new A();
}
//1. @Deprecated 修饰某个元素(包、类、方法、元素、等),表示该元素已经过时
//2.即不在推荐使用,但是仍然可以使用
//3、作用:jdk版本会更新,可能有的方法现在是jdk8很流行,然后jdk升级到10了,有些方法会过时,那可以直接淘汰吗?
// 当然不行,如果直接淘汰,那么之前写的代码就会出现问题,所以就要存在过渡,不推荐用,但还是能用
@Deprecated
class A{
public void f(){
System.out.println(" ");
}
}
@SuppressWarnings
@SuppressWarnings:抑制编译器警告
package 枚举和注解.注解;
import java.util.ArrayList;
import java.util.List;
public class Demo3 {
@SuppressWarnings({"all"})//抑制所有警告,平时我们写代码,会存在很多警告不美观,直接all
public static void main(String[] args) {
//1. 当我们不希望看到这些警告的时候,可以使用SuppressWarnings注解来抑制警告信息
//2. 在{""}中,可以写入你希望抑制(不显示)警告信息
//3. 作用范围,放在main方法这里,那么只对main方法有效;当然也可以直接方法类、属性上
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
int i;
}
}
元注E解的基本介绍
JDK的元Annotation用于修饰其他Annotation
元注解:本身作用不大,讲这个原因希望同学们,看源码时,可以知道他是干什么.
元注解的种类(使用不多,了解,不用深入研究)
- Retention //指定注解的作用范围,三种SOURCE,CLASS,RUNTIME
- Target //指定注解可以在哪些地方使用
- Documented //指定该注解是否会在javadoc体现
韩顺平 - Inherited //子类会继承父类注解
教育
暂时实现的注解还没有实现什么功能
反射
问题引入:
现在给你一个文件,里面写了一个类的全路径,和这类的相关方法,现在需要你创建这个类的对象,并调用这个方法;
根据我们以前学的内容直接new 类名();但不在同一路径下的类显然不能直接创建对象;
可能又会想到,可以把类的路径读出来,然后根据这个路径new对象,但读到的内容是一个字符串,可以用字符串直接new对象吗?显然不行
那怎么办呢?这就需要我们今天的学习内容。可能暂时不知道为啥会有这样的问题,但学到后面的框架时,这样的问题将会很多。
快速入门
接下来根据上面的需求,实现以下代码,先体验一下
package 反射;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Cat {
public void eat(){
System.out.println("猫吃鱼");
}
public void sing(){
System.out.println("喵喵喵");
}
}
//假设只给你一个文件里面有这个类的绝对路径,以及相关方法,让你创建这个类,怎么办?
/*
首先把相关信息读出来,这部分就不演示了,直接给出
classPath = "反射.Cat.java"
method = "eat"
*/
class Test{
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
String classPath = "反射.Cat";
String methodName = "eat";
//(1) 加载类,返回Class类型的对象cls
Class cls = Class.forName (classPath);//这里可能存在字符串读不出类,所以需要抛异常
//(2) 通过cls得到你加载的类反射.Cat.java 的对象实例
Object o = cls.newInstance();//抛异常
System.out.println("o的运行类型=" + o.getClass()); //运行类型
//(3) 通过cls得到你加载的类反射.Cat.java 的methodName"eat" 的方法对象
//即:在反射中,可以把方法视为对象( 万物皆对象)
Method method1 = cls.getMethod (methodName) ;
//(4) 通过method1 调用方法:即通过方法对象来实现调用方法
System.out.println("=============================") ;
method1.invoke(o); //传统方法对象。方法(),反射机制方法。invoke(对象)
//如果需要调用这个类中的其他方法,也不用修改代码,直接在信息文件中修改即可
//这里存在很多注释,实现的话也就时4步
}
}
反射机制
1.反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到
2.加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象) ,这个对象包含了类的完整结构信息。通过这个对象得到类的结构。这个Class对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之为:反射可能大家对Class类感觉很奇怪,这里没啥奇怪的,就和我们平常接触到的类时一样的,比如Math、Arrays…,只不过这个类的名字很奇怪;
就像是他的名字叫张三,你的名字叫李四,我的名字就叫名字,诶你说气不气
图解
反射相关的主要类:
- java.lang.Class:代表一 个类,Class对象表示某个类加载后在堆中的对象
- java.lang.reflect.Method:代表类的方法,Method对象表示某个类的方法
- java.lang.reflect.Field:代表类的成员变量,Field对象表示某个类的成员变量
- java.lang.reflect.Constructor:代表类的构造方法,Constructor对象表示构造器
package 反射;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Cat {
public void eat(){
System.out.println("猫吃鱼");
}
public void sing(){
System.out.println("喵喵喵");
}
}
class Test{
public static void main(String[] args) throws Exception{
String classPath = "反射.Cat";
String methodName = "eat";
//加载类,返回Class类型的对象cls
Class cls = Class.forName (classPath);//这里可能存在字符串读不出类,所以需要抛异常
//通过cls得到你加载的类反射.Cat.java 的对象实例
Object o = cls.newInstance();//抛异常
System.out.println("o的运行类型=" + o.getClass());
//得到方法
Method method1 = cls.getMethod (methodName) ;
method1.invoke(o); //调用方法
//得到成员变量,getField不能得到私有的属性
Field nameField = cls.getField("age"); //
System.out.println(nameField.get(o)); //调用变量
//构造方法
Constructor constructor = cls.getConstructor() ;//里面可以传参数,不传参数得到的就是无参构造方法
System.out.println(constructor);
}
}
反射优点和缺点
1.优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架技术就失去底层支撑。
2.缺点:使用反射基本是解释执行,对执行速度有影响.
那么怎么优化呢? 反射调用优化+关闭访问检查
●反射调用优化关闭访问检查
- Method和Field、 Constructor对象都有setAccessible(方法
- setAccessible作用是启动和禁用访问安全检查的开关
3.参数值为true表示反射的对象在使用时取消访问检查,提高反射的效率。参数值
为false则表示反射的对象执行访问检查
以调用方法为例,只需要在Method method1 = cls.getMethod (methodName) ;时,再加一句method1.setAccessible(true);但并不会优化很多,和我们以前调用方法还是差了很多
- Class也是类,因此也继承Object类[类图]
- Class类对象不是new出来的,而是系统创建的[演示]
- 对于某个类的Class类对象, 在内存中只有一份,因为类只加载一 次[演示]
4.每个类的实例都会记得自己是由哪个Class实例所生成
5.通过Class对象可以完整地得到一个类的完整结构,通过一系列API - Class对象是存放在堆的
7.类的字节码二进制数据,是放在方法区的,
有的地方称为类的元数据(包括方法代码,
变量名,方法名,访问权限等等) https:/ /www.zhihu.com/question/38496907
常用方法
package 反射;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Cat {
public String name = "花花";
public void eat(){
System.out.println("猫吃鱼");
}
public void sing(){
System.out.println("喵喵喵");
}
}
class Test{
public static void main(String[] args) throws Exception{
String classPath = "反射.Cat";
//1。获取到Car类对应的Class对象
//<?>表示不确定的Java类型
Class<?> cls = Class.forName (classPath) ;
//2. 输出cLs
System.out.println(cls); // 显示cls对象,是哪个类的Class对象 反射.Cat
System.out.println(cls.getClass()); //输出cLs运行类型java.Lang.Class
//3. 得到包名
System.out.println(cls.getPackage().getName());//包名
//4.得到全类名
System.out.println(cls. getName());
//5. 通过cLs创建对象实例
Cat cat = (Cat)cls.newInstance() ;
System.out.println(cat);
//6.通过反射获取属性brand
Field brand = cls. getField("name");
System.out.println(brand.get(cat));//花花
//7.通过反射给属性赋值
brand.set(cat, "小白");
System.out.println(brand.get(cat));//小白
//8我希望大家可以得到所有的属性(字段)
Field[] fieLds = cls.getFields();//得到所有的变量名
for (Field f : fieLds) {
System.out.println(f.getName());//名称
}
}
}
获取class类对象的方式
package 反射;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Cat {
public String name = "花花";
public void eat(){
System.out.println("猫吃鱼");
}
public void sing(){
System.out.println("喵喵喵");
}
}
class Test{
public static void main(String[] args) throws Exception {
//1、Class.forName()就是上面的例子 多用于配置文件,读取类全路径,加载类. 编译阶段
String classPath = "反射.Cat";
Class<?> cls1 = Class.forName(classPath);
System.out.println(cls1);
//2、类名.class 多用于参数传递,比如通过反射得到对应构造器对象. 加载阶段
Class cls2 = Cat.class;
System.out.println(cls2);
//3、对象.getClass() 通过创建好的对象,获取Class对象. 运行阶段
Cat cat = new Cat();
Class cls3 = cat.getClass();
System.out.println(cls3);
//4. 通过类加载器[4种]来获取到类的CLass对象
//(1)先得到类加载器car
ClassLoader classLoader = cat.getClass().getClassLoader() ;
//(2)通过类加载器得到CLass对象
Class cls4 = classLoader.loadClass (classPath);
System.out.println(cls4);
//cls1、cls2、cls3、cls4 其实是同一个对象
System.out.println(cls1.hashCode());
System.out.println(cls2.hashCode());
System.out.println(cls3.hashCode());
System.out.println(cls4.hashCode());
//5.基本数据(int, char, booLean, float , double, byte, long, short) 按如下方式得到Class类对象
Class<Integer> integerClass = int.class;
Class<Character> characterClass = char.class;
Class<Boolean> booLeanCLass = boolean.class;
System.out.println(integerClass);//int
//6.基本数据类型对应的包装类,可以通过.TYPE 得到CLass类对象
Class<Integer> type1 = Integer. TYPE;
Class<Character> type2 = Character. TYPE;
System.out.println(type1);
System.out.println(integerClass.hashCode());//?
System.out.println(type1.hashCode());//? 相等
}
}
哪些类型有class对象
1.外部类,成员内部类,静态内部类,局部内部类,匿名内部类
2。interface :接口
3.数组
4enum :枚举
5.annotation :注解
6.基本数据类型
7void
package 反射;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Cat {
public String name = "花花";
public void eat(){
System.out.println("猫吃鱼");
}
public void sing(){
System.out.println("喵喵喵");
}
}
class Test{
public static void main(String[] args) throws Exception {
Class<String> cls1 = String.class;//外部类
Class<Serializable> cls2 = Serializable.class;//接口
Class<Integer[]> cls3 = Integer[].class;//数组
Class<float[][]> cls4 = float[][].class;//二维数组
Class<Deprecated> cls5 = Deprecated.class;//注解
//枚举
Class<Thread.State> cls6 = Thread.State.class;
Class<Long> cls7 = Long.class;//基本数据类型
Class<Void> cls8 = void.class;//void数据类型
Class<Class> cls9 = Class.class;//类
System.out.println(cls1);
System.out.println(cls2);
System.out.println(cls3);
System.out.println(cls4);
System.out.println(cls5);
System.out.println(cls6);
System.out.println(cls7);
System.out.println(cls8);
System.out.println(cls9);
}
}
反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载。
1.静态加载:编译时加载相关的类,如果没有则报错,依赖性太强(new 类)
2.动态加载:运行时加载需要的类,如果运行时不用该类,则不报错,降低了依赖性(forName)
●类加载时机
1.当创建对象时(new) //静态加载
2.当子类被加载时,父类也加载//静态加载
3.调用类中的静态成员时//静态加载
4.通过反射//动态加载