在经过一次没有准备的面试后,发现自己虽然写了两年的android代码,基础知识却忘的差不多了。这是程序员的大忌,没有扎实的基础,就只能是copy别人的代码,实现自己的业务。永远只是一个最底层的corder。这次的失败,让我刻骨铭心!也让我下决心做一个有思想的程序员!
这是我的开篇之作,从java基础开始学习。本篇文章大多摘自张拭心的博客 shixinzhang,结识张同学,是因为他在gitchat上分享了他的面试经验,再看了他的博客文章,从此深深的膜拜!感谢在迷茫的时刻遇到了你
以下是java基础:
1.内部类、静态内部类区别、使用场景:
答:
Inner类的实例有Outer的实例的指针(即可以访问Outer的成员)。而StaticInner类没有。
内部类的重要作用在于为多重继承提供支持。
简单理解就是:如果把类比喻成鸡蛋,内部类为蛋黄,外部类是蛋壳。那么静态类相当于熟鸡蛋,就算蛋壳破碎(外部类没有实例化),蛋黄依然完好(内部类可以实例化);而非静态类相当于生鸡蛋,蛋壳破碎(无实例化),蛋黄也会跟着xx(不能实例化)。
2.抽象类、接口 继承实现区别:
答:
包含抽象方法的类称为抽象类,但并不意味着抽象类中只能有抽象方法,它和普通类一样,同样可以拥有成员变量和普通的成员方法。注意,抽象类和普通类的主要有三点区别:
1)抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public。
2)抽象类不能用来创建对象;
3)如果一个类继承于一个抽象类,则子类必须实现父类的抽象方法。如果子类没有实现父类的抽象方法,则必须将子类也定义为为abstract类。
接口中所有的方法不能有具体的实现,也就是说,接口中的方法必须都是抽象方法。从这里可以隐约看出接口和抽象类的区别,接口是一种极度抽象的类型,它比抽象类更加“抽象”,并且一般情况下不在接口中定义变量。
语法层面上的区别
1)抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法;
2)抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的;
3)接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;
4)一个类只能继承一个抽象类,而一个类却可以实现多个接口。
设计层面上的区别
1)抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。
2)抽象类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计。
接口可以继承接口
抽象类可以实现接口(不需要实现接口中的方法,接口是一种特殊的抽象类)
普通类继承抽象类或实现接口
普通类继承抽象类:必须实现抽象类的所有抽象方法和该抽象类父类的所有方法
普通类实现接口:必须实现接口及其父接口的全部方法
3.集合
1.Iterator 是一个集合上的迭代器:可以遍历删除集合元素
2.CopyOnWriteArrayList 集合快照。通过增强for循环进行删除集合元素
3.ListIterator 集合迭代器 与Iterator的区别:
1. ListIterator有add()方法,可以向List中添加对象,而Iterator不能
2. ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,
但ListIterator 有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。
Iterator就不可以。
3. ListIterator可以定位当前的索引位置,nextIndex()和previousIndex()可以实现。
Iterator没有此功能。
4. 都可实现删除对象,但是ListIterator可以实现对象的修改,set()方法可以实现。
Iierator仅能遍历,不能修改。
4.Collection 集合的操作:
1.Aggregate Operations 聚合操作:结合lambda使用
Stream<E> stream() 获取连续流
Stream<E> parallelStream() 获取并行流
2.并发和并行的概念
5.List集合:List 是一个元素有序的、可以重复、可以为 null 的集合(set不可重复)
6.ArrayList: 本质是一个数组,在元素变化时new一个新的数组
7.LinkedList: 本质是一个双向链表,Link可以动态添加删除元素
8.List list3 = new ArrayList(list1);list3.addAll(list2);将两个集合组合在一起,引用地址
没有变。
9.List 与 Array的区别:
相似之处:
都可以表示一组同类型的对象
都使用下标进行索引
不同之处:
数组可以存任何类型元素
List 不可以存基本数据类型,必须要包装
数组容量固定不可改变;List 容量可动态增长
数组效率高; List 由于要维护额外内容,效率相对低一些
10.List 与 Array相互转换:
Arraylist 效率大于LinkList
11.集合的工具类 Collections 中包含很多 List 的相关操作算法:
sort ,归并排序
shuffle ,随机打乱
reverse ,反转元素顺序
swap ,交换
binarySearch ,二分查找
……
12.AbstractCollection(abstract):是Java集合框架中Collection接口的一个直接实
现类,Collection下的大多数子类都继承AbstractCollection,比如List的实现类,
Set的实现类。
13.AbstractList (abstract):继承自 AbstractCollection 抽象类,实现了 List 接口,是
ArrayList 和AbstractSequentiaList 的父类
1.RandomAccess:标记支持随机访问 实现的有:ArrayList, AttributeList,
CopyOnWriteArrayList, Vector, Stack
2.AbstractList特征:
既实现了 List 的期望
也继承了 AbstractCollection 的传统
还创建了内部的迭代器 Itr, ListItr
还有两个内部子类 SubList 和 RandomAccessSublist;
15.ArrayList: 继承AbstractList实现list<>
迭代器遍历,如果是并发的环境需要加同步锁
List list = Collections.synchronizedList(new ArrayList(...));
16.AbstractSequentialList:继承自 AbstractList,是 LinkedList 的父类,是 List 接口 的简
化版实现,只支持按次序访问,实现了ListIterator相关的方法

- Queue 队列:队列是数据结构中比较重要的一种类型,它支持 FIFO,尾部添加、
头部删除(先进队列的元素先出队列),跟我们生活中的排队类似。继承
Collection 接口 ,Deque, LinkedList, PriorityQueue, BlockingQueue 等类都实现
了它,Queue 用来存放 等待处理元素 的集合,这种场景一般用于缓冲、并发访问
18.Queue中的方法:后面的方法在发生异常是不会报错
1.add(E), offer(E) 在尾部添加: false
2.remove(), poll() 删除并返回头部:null
3.element(), peek() 获取但不删除: null
注意事项:
1.虽然 LinkedList 没有禁止添加 null,但是一般情况下 Queue 的实现类
都不允许添加 null 元素,为啥呢?因为 poll(), peek() 方法在异常的时候
会返回 null,你添加了 null 以后,当获取时不好分辨究竟是否正确返
回。
2.Queue 一般都是 FIFO 的,但是也有例外,比如优先队列 priority
queue(它的顺序是根据自然排序或者自定义 comparator 的);再比如
LIFO 的队列(跟栈一样,后来进去的先出去)。不论进入、出去的先
后顺序是怎样的,使用 remove(),poll() 方法操作的都是 头部 的元素;
而插入的位置则不一定是在队尾了,不同的 queue 会有不同的插入逻
辑。
19.Deque :是 Double ended queue (双端队列) 的缩写,读音和 deck 一样,蛋壳。
一般场景
LinkedList 大小可变的链表双端队列,允许元素为 null
ArrayDeque 大下可变的数组双端队列,不允许 null
并发场景
LinkedBlockingDeque 如果队列为空时,获取操作将会阻塞,知道有元素添加
工作密取模式:在生产者-消费者模式中所有消费者都从一个工作队列中取元素,一般使用阻塞队 列实现;而在工作密取模式中,每个消费者有其单独的工作队列,如果它完成了自己双端队列中的 全部工作,那么它就可以从其他消费者的双端队列末尾秘密地获取工作。工作密取模式对比传统的生产者-消费者 模式,更为灵活,因为多个线程不会因为在同一个工作队列中抢占内容发生竞争。在大多数时候,它们只是访问自己的双端队列。即使需要访问另一个队列时,也是从 队列的尾部获取工作,降低了队列上的竞争程度。
20.LinkedList:继承自 AbstractSequentialList 接口,同时了还实现了 Deque, Queue
接口。
特点:
双向链表实现
元素时有序的,输出顺序与输入顺序一致
允许元素为 null
所有指定位置的操作都是从头开始遍历进行的
和 ArrayList 一样,不是同步容器List list = Collections.synchronizedList(new LinkedList(...));
区别:
ArrayList
基于数组,在数组中搜索和读取数据是很快的。因此 ArrayList 获取数据的时间复杂度是O(1);
但是添加、删除时该元素后面的所有元素都要移动,所以添加/删除数据效率不高;
另外其实还是有容量的,每次达到阈值需要扩容,这个操作比较影响效率。
LinkedList
基于双端链表,添加/删除元素只会影响周围的两个节点,开销很低;
只能顺序遍历,无法按照索引获得元素,因此查询效率不高;
没有固定容量,不需要扩容;
需要更多的内存, LinkedList 每个节点中需要多存储前后节点的信息,占用空间更多些。
21.Vector: Vector 和 ArrayList 一样,都是继承自 AbstractList。它是 Stack 的父类。英文的意
思是 “矢量”
Vector 特点
底层由一个可以增长的数组组成
Vector 通过 capacity (容量) 和 capacityIncrement (增长数量) 来尽量少的占用空间
扩容时默认扩大两倍
最好在插入大量元素前增加 vector 容量,那样可以减少重新申请内存的次数
通过 iterator 和 lastIterator 获得的迭代器是 fail-fast 的
通过 elements 获得的老版迭代器 Enumeration 不是 fail-fast 的
同步类,每个方法前都有同步锁 synchronized
在 JDK 2.0 以后,经过优化,Vector 也加入了 Java 集合框架大家族
Vector VS ArrayList
共同点:
都是基于数组
都支持随机访问
默认容量都是 10
都有扩容机制
区别:
Vector 出生的比较早,JDK 1.0 就出生了,ArrayList JDK 1.2 才出来
Vector 比 ArrayList 多一种迭代器 Enumeration
Vector 是线程安全的,ArrayList 不是
Vector 默认扩容 2 倍,ArrayList 是 1.5
如果没有线程安全的需求,一般推荐使用 ArrayList,而不是 Vector,因为每次都要获取
锁,效率太低。
21.Stack :Stack继承自 Vector,它是 数组实现的栈。
Stack 具有以下特点:
继承自 Vector
有 5 种创建 Stack 的方法
采用数组实现
除了 push(),剩下的方法都是同步的
其实 LinkedList 这个栈的特性也是继承自 双端队列 Deque,官方也推荐在使用栈时优先使
用 Deque,而不是 Stack
22.Map:Map 接口 是和 Collection 接口 同一等级的集合根接口,它 表示一个键值对 (key-
value) 的映射。类似数学中 函数 的概念
1.Map 中元素的顺序取决于迭代器迭代时的顺序,有的实现类保证了元素输入输出时的
顺序,比如说 TreeMap;有的实现类则是无序的,比如 HashMap。
2.KeySet: KeySet 是一个 Map 中键(key)的集合,以 Set 的形式保存,不允许重复,
因此键存储的对象需要重写 equals() 和 hashCode() 方法。
3.Values 是一个 Map 中值 (value) 的集合,以 Collection 的形式保存,因此可以重
复。
4.Entry : Entry 是 Map 接口中的静态内部接口,表示一个键值对的映射,通过
Map.entrySet()方法获得的是一组 Entry 的集合,保存在 Set 中,所以 Map 中
的 Entry 也不能重复
getKey() , 获取这组映射中的键 key
getValue() , 获取这组映射中的值 value
setValue() , 修改这组映射中的值
hashCode() , 返回这个 Entry 的哈希值
equals() , 对比 key-value 是否相等
5.Map的三种遍历方式:
Set set = map.keySet(); for (Object key : set) { System.out.println(map.get(key)); }
Set entrySet = map.entrySet(); for (Object o : entrySet) { Map.Entry entry = (Map.Entry) o; System.out.println(entry); //key=value System.out.println(entry.getKey() + " / " + entry.getValue()); }
Collection values = map.values(); Iterator iterator = values.iterator(); while (iterator.hasNext()){ System.out.println("value " + iterator.next()); }
6.Map 的实现类主要有 4 种:
Hashtable
古老,线程安全
HashMap
速度很快,但没有顺序
TreeMap
有序的,效率比 HashMap 低
LinkedHashMap
结合 HashMap 和 TreeMap 的有点,有序的同时效率也不错,仅比 HashMap 慢一点
7.Map特点:
没有重复的 key
每个 key 只能对应一个 value, 多个 key 可以对应一个 value
key,value 都可以是任何引用类型的数据,包括 null
Map 取代了古老的 Dictionary 抽象类
注意:
可以使用 Map 作为 Map 的值,但禁止使用 Map 作为 Map 的键。因为在这么复
杂的 Map 中,equals() 方法和 hashCode() 比较难定义,另一方面,你应该尽量避
免使用“可变”的类作为 Map 的键。如果你将一个对象作为键值并保存在 Map 中,
之后又改变了其状态,那么 Map 就会产生混乱,你所保存的值可能丢失。
22.Hash:哈希又称“散列”。散列(hash)英文原意是“混杂”、“拼凑”、“重新表述”的意思
1.通常使用数组或者链表来存储元素,一旦存储的内容数量特别多,需要占用很大的空
间,而且在查找某个元素是否存在的过程中,数组和链表都需要挨个循环比较,而通
过 哈希 计算,可以大大减少比较次数
2.哈希 其实是随机存储的一种优化,先进行分类,然后查找时按照这个对象的分类去
找。通过一次计算大幅度缩小查找范围,自然比从全部数据里查找速度要快
3.哈希函数:哈希函数是一种映射关系,根据数据的关键词 key ,通过一定的函数关系,
计算出该元素存储位置的函数(直接定址法,除留余数法,数字分析法,随机数法...)
4.哈希冲突:选用哈希函数计算哈希值时,可能不同的key会得到相同的结果,一个地址
怎么存放多个数据呢?这就是冲突(链接法(拉链法),开放定址法,线性探查法)
5.哈希表:实现关联数组(associative array)的一种数据结构,广泛应用于实现数据的
快速查找
6.分布式缓存:网络环境下的分布式缓存系统一般基于一致性哈希(Consistent
hashing)。简单的说,一致性哈希将哈希值取值空间组织成一个虚拟的环,各个服务
器与数据关键字K使用相同的哈希函数映射到这个环上,数据会存储在它顺时针“游走”遇
到的第一个服务器。可以使每个服务器节点的负载相对均衡,很大程度上避免资源的浪
费。在动态分布式缓存系统中,哈希算法的设计是关键点。使用分布更合理的算法可以
使得多个服务节点间的负载相对均衡,可以很大程度上避免资源的浪费以及部分服务器
过载。 使用带虚拟节点的一致性哈希算法,可以有效地降低服务硬件环境变化带来的数
据迁移代价和风险,从而使分布式缓存系统更加高效稳定。
23.AbstractMap:AbstractMap是Map接口的的实现类之一,也是HashMap, TreeMap,
ConcurrentHashMap 等类的父类。AbstractMap 提供了 Map 的基本实现,使得我们以
后要实现一个 Map 不用从头开始,只需要继承 AbstractMap, 然后按需求实现/重写对应
方法即可。
24.HashMap:HashMap 是一个采用哈希表实现的键值对集合,继承自 AbstractMap,实现了
Map接口 。
1.特点:
底层实现是 链表数组,JDK 8 后又加了 红黑树
实现了 Map 全部的方法
key 用 Set 存放,所以想做到 key 不允许重复,key 对应的类需要重写 hashCode 和 equals 方法
允许空键和空值(但空键只有一个,且放在第一位,下面会介绍)
元素是无序的,而且顺序会不定时改变
插入、获取的时间复杂度基本是 O(1)(前提是有适当的哈希函数,让元素分布在均匀的位置,发生 冲突采用拉链法解决)
遍历整个 Map 需要的时间与 桶(数组) 的长度成正比(因此初始化时 HashMap 的容量不宜太大)
两个关键因子:初始容量(16)、加载因子(0.75)
除了不允许null 并且同步 Hashtable几乎跟HashMap相同
2.当 HashMap 中有大量的元素都存放到同一个桶中时,这时候哈希表里只有一个桶,
这个桶下有一条长长的链表,这个时候 HashMap 就相当于一个单链表,假如单链表
有 n 个元素,遍历的时间复杂度就是 O(n),完全失去了它的优势。
3.为什么哈希表的容量一定要是 2的整数次幂? :使用减法替代取模,提升计算效率;
为了使不同 hash 值发生碰撞的概率更小,尽可能促使元素在哈希表中均匀地散列。
4.HashMap 中 equals() 和 hashCode() 有什么作用? :HashMap 的添加、获取时需要
通过key的hashCode()进行hash(),然后计算下标( n-1 & hash),从而获得要找的同的
位置。当发生冲突(碰撞)时,利用 key.equals() 方法去链表或树中去查找对应节点
1.8jdk支持红黑树结构特点:
1.添加时,当桶中链表个数超过 8 时会转换成红黑树;
2.删除、扩容时,如果桶中结构为红黑树,并且树中元素个数太少的话,会进行修
剪或者直接还原成链表结构;
3.查找时即使哈希函数不优,大量元素集中在一个桶中,由于有红黑树结构,性能
也不会差。
- 红黑树 : 红黑树本质上是一种二叉查找树,但它在二叉查找树的基础上额外添加了一
个标记(颜色),同时具有一定的规则。这些规则使红黑树保证了一种平衡,插入、
删除、查找的最坏时间复杂度都为 O(logn)。
特点:
1.每个节点要么是红色,要么是黑色;
2.根节点永远是黑色的;
3所有的叶节点都是是黑色的(注意这里说叶子节点其实是上图中的 NIL 节点);
4.每个红色节点的两个子节点一定都是黑色;
5.从任一节点到其子树中每个叶子节点的路径都包含相同数量的黑色节点
4.注解
1.注解是一种元数据(描述数据的数据)
2.描述作用,不会直接生效,需要在编译前/运行时获取注解信息代码检查
3.Java 内置的注解:
5个用于通知编译器信息的注解
@Override :空注解,用于标记那些覆盖父类方法的方法,如果父类没有这个方法,或者复
写的方法访问权限比父类的权限小,编译器就会报错
@Deprecated : 空注解,用于标记那些不应该被使用的代码,如果使用了过时的代码,编译
器会发出警告
@SafeVarargs : 空注解,(varargs 可变参数)用于标记构造函数或者方法,通知编译器,
这里的可变参数相关的操作保证安全
@FunctionInterface : Java SE 8 出现的,用于通知编译器,这个类型是 function 接口
@SuppressWarning:抑制错误,可以用于标记整个类、某个方法、某个属性或者某个参
数,用于告诉编译器这个代码是安全的,不必警告
强烈建议最小范围使用这个注解,一旦你在一个比较大的范围抑制错误,可能会把真正的问
题掩盖了
4 个用于修饰注解的注解
@Documented:让注解信息出现在 document 中
@Retention : 指出注解如何存储,支持以下三种参数
RetentionPolicy.SOURCE : 注解只保留在源码中,编译时会忽略
RetentionPolicy.CLASS : 更高一级,编译时被编译器保留,但是运行时会被 JVM 忽略
RetentionPolicy.RUNTIME : 最高级,运行时会被保留,可以被运行时访问
@Target :指出注解作用于(修饰)什么对象,支持以下几种参数
ElementType.TYPE : 作用于任何类、接口、枚举
ElementType.FIELD : 作用于一个域或者属性
ElementType.METHOD : 作用于一个方法
ElementType.PARAMTER : 作用于参数
ElementType.CONSTRUCTOR : 作用于构造函数
ElementType.LOCAL_VARIABLE : 作用于本地变量
ElementType. ANNOTATION_TYPE : 作用于注解
ElementType.PACKAGE : 作用于包
@Inherited :当前注解是否可以继承
- int value()的使用||default的使用
5.注解的作用:
编译前提示信息:注解可以被编译器用来发现错误,或者清除不必要的警告;
编译时生成代码:一些处理器可以在编译时根据注解信息生成代码,比如 Java 代码,xml
代码等;
运行时处理:我们可以在运行时根据注解,通过反射获取具体信息,然后做一些操作。
6.注解的使用:
自定义注解:规定处理对象类型,保存阶段,以及包含的值
使用注解修饰我们想要的处理的类、方法或者属性
读取注解,使用注解处理器处理(运行时处理器,编译时处理面试的java基础器)
7.运行时处理器:注解 + 反射 ,通过反射得到注解方法需要的值
8.APT;即 Annotation Processing Tool,注解处理工具,它可以在编译时检测源代码文件,找到
符合条件的注解修饰的内容,然后进行相应的处理。
9.编译时生成代码过程:
先创建注解
创建注解处理器,在其中拿到注解修饰的变量信息,生成需要的代码
创建运行时,调用生成代码的调度器
10.编译时生成代码的使用:
用注解修饰变量
编译时使用注解处理器生成代码
运行时调用生成的代码
11.MyButterknife的实现
5.反射
- Reflection(反射)Introspection(内省)区别:
内省在运行时检查一个对象的类型或者属性:instancesof
反射在运行时检查或者修改一个对象信息:例如访问私有方法,动态创建对象
3.反射的入口: java.lang.Class
基本类型,(固定的 8 种) :
整数:byte, short, int, long
小数:float, double
字符:char
布尔值:boolean
引用类型 :
所有的引用类型都继承自 java.lang.Object
类,枚举,数组,接口都是引用类型
java.io.Serializable 接口,基本类型的包装类(比如 java.lang.Double)也是引用类型
对每一种对象,JVM 都会实例化一个 java.lang.Class 的实例,java.lang.Class 为我们提供
了在运行时访问对象的属性和类型信息的能力。Class 还提供了创建新的类和对象的能力。
4.如何得到一个Class对象:
1.Object.getClass 方法:仅限于引用类型
2.类名.class 语法: int.class.newInstance()不仅能用于引用类型,基本类型也可以
3.Class.forName():Class<?> c = Class.forName("java.lang.String");通过完整的类路径得
到,仅限于引用类型
4.静态属性 TYPE:Class<Void> voidWrapper = Void.TYPE; 不仅能用于引用类型,基本
类型也可以
5.返回 Class 的方法:
1.Class.getSuperclass() 返回调用类的父类
2.Class.getClasses() 返回调用类的所有公共类、接口、枚举组成的 Class 数组,包括
继承的
3.Class.getDeclaredClasses() 返回调用类显式声明的所有类、接口、枚举组成的
Class 数组
4.Class.getDeclaringClass() 返回类
5.java.lang.reflect.Field.getDeclaringClass() 属性
6.java.lang.reflect.Method.getDeclaringClass() 方法
7.java.lang.reflect.Constructor.getDeclaringClass() 构造器所在的类
6.Class 的修饰符:
1.访问权限控制符:public, protected, private
2.抽象的、需要实现的:abstract
3.限制只能有一个实例的:static
4.不允许修改的:final
5.线程同步锁:synchronized
6.原生函数:native
7.采用严格的浮点精度:strictfp
8.接口
9.注解
通过 Class.getModifiers() 获得调用类的修饰符的二进制值,使用 Modifier.toString(int
modifiers) 将二进制值转换为字符串
Interface 默认是 abstract 的,虽然我们没有添加,编译器会在编译器为每个 Interface
添加这个修饰符。
只有被 @Retention(RetentionPolicy.RUNTIME) 修饰的注解才可以在运行时被发射获
取
Java 中预定义的注解 @Deprecated,@Override, 和 @SuppressWarnings 中只有
@Deprecated 可以在运行时被访问到
7.Class 的成员:
1.java.lang.reflect.Constructor:表示该 Class 的构造函数
2.java.lang.reflect.Field:表示该 Class 的成员变量
3.java.lang.reflect.Method:表示该 Class 的成员方法
8.Field 成员变量的反射:
1.获取成员变量的类型:Filed.getType();Filed.getGenericType()
2.获取成员变量的修饰符: 同上,线程共享数据的一致性:volatile
3.获取和修改成员变量的值:得到Filed 然后,file.set()就可以修改成员变量的值
4.常见的错误:
1.java.lang.IllegalArgumentException:使用反射获取或者修改一个变量
值时,编译器不会进行自动装/拆箱,需要自己包装
2.反射非 public的变量导致的 NoSuchFieldException
3.修改 final类型的变量导致的 IllegalAccessException:设置setAccessible(true)
4.在使用反射修改某个对象的成员变量前你要明白,这样做会造成一定程度的性能开
销,因为在反射时这样的操作需要引发许多额外操作,比如验证访问权限等
9.Method 方法的反射:
1.继承的方法(包括重载、重写和隐藏的)会被编译器强制执行,这些方法都无法反
射。
2.方法的组成:每个方法都由 修饰符、返回值、参数、注解和抛出的异常组成
3.如何获取方法的信息: Method[] declaredMethods = cls.getDeclaredMethods();
declaredMethod.getName()); //获得单独的方法名
declaredMethod.toGenericString() //获得完整的方法信息(包括修饰符、返回
值、路径、名称、参数、抛出值)
declaredMethod.getModifiers(); //获得修饰符
declaredMethod.getReturnType()); //获得返回值
declaredMethod.getGenericReturnType()); //获得完整信息的返回值
declaredMethod.getParameterTypes(); //获得参数类型
declaredMethod.getExceptionTypes(); //获得异常名称
declaredMethod.getAnnotations(); //获得注解
4.获得的方法有三种类型
synthetic method:合成方法 内部类生成的方法
varargs ( variable arguments) method:Java 可变参数方法
bridge method:桥接方法 兼容jdk1.5之前,擦除泛型
5.反射调用方法:Method.invoke();
6.泛型
1.“泛型” 意味着编写的代码可以被不同类型的对象所重用。泛型的本质是参数化类
型,也就是说所操作的数据类型被指定为一个参数
2.Object处理不同类型时的缺点:
每次使用时都需要强制转换成想要的类型
在编译时编译器并不知道类型转换是否正常,运行时才知道,不安全
3.“泛型”引入的目的:
1.类型安全
泛型的主要目标是提高 Java 程序的类型安全
编译时期就可以检查出因 Java 类型不正确导致的 ClassCastException 异常
符合越早出错代价越小原则
2.消除强制类型转换
泛型的一个附带好处是,使用时直接得到目标类型,消除许多强制类型转换
所得即所需,这使得代码更加可读,并且减少了出错机会
潜在的性能收益
3.由于泛型的实现方式,支持泛型(几乎)不需要 JVM 或类文件更改
所有工作都在编译器中完成
编译器生成的代码跟不使用泛型(和强制类型转换)时所写的代码几乎一致,
只是更能确保类型安全而已
4."泛型" 的使用方式:类型参数的意义是告诉编译器这个集合中要存放实例的类型,
从而在添加其他类型时做出提示,在编译时就为类型安全做了保证。
1.泛型类:泛型类和普通类的区别就是类名后有类型参数列表 <E>如:
public class HashMap<K, V> 泛型类最常见的用途就是作为容纳不同类型数
据的容器类,比如 Java 集合容器类。
2.泛型接口:泛型接口在接口名后添加类型参数,接口声明类型后,接口方法就可
以直接使用这个类型。实现类在实现泛型接口时需要指明具体的参数类型,不
然默认类型是 Object,这就失去了泛型接口的意义
泛型接口比较实用的使用场景就是用作策略模式的公共策略,泛型接口定
义基本的规则,然后作为引用传递给客户端,这样在运行时就能传入不
同的策略实现类。
3.泛型方法:泛型方法是指使用泛型的方法,如果它所在的类是个泛型类,
那就很简单了,直接使用类声明的参数。如果一个方法所在的类不是泛
型类,或者他想要处理不同于泛型类声明类型的数据,那它就需要自己
声明类型
4.泛型的通配符:有时候希望传入的类型有一个指定的范围,从而可以进行
一些特定的操作,泛型中有三种通配符形式:
<?> 无限制通配符
<? extends E> extends 关键字声明了类型的上界,表示参数化的类型可
能是所指定的类型,或者是此类型的子类(用于灵活读取,使得方法
可以读取 E 或 E 的任意子类型的容器对象)
<? super E> super 关键字声明了类型的下界,表示参数化的类型可能是
指定的类型,或者是此类型的父类(用于灵活写入或比较,使得对象
可以写入父类型的容器,使得父类型的比较方法可以应用于子类对
象)
5.泛型的类型擦除:当编译器对带有泛型的java代码进行编译时,它会去执
行类型检查和类型推断,然后生成普通的不带泛型的字节码,这种普通的字
节码可以被一般的 Java 虚拟机接收并执行,这在就叫做 类型擦除(type
erasure)。泛型就是一个语法糖,它运行时没有存储任何类型信息.
6.泛型的规则:
泛型的参数类型只能是类(包括自定义类),不能是简单类型。
同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本
的泛型类实例是不兼容的。
泛型的类型参数可以有多个
泛型的参数类型可以使用 extends 语句,习惯上称为“有界类型”
泛型的参数类型还可以是通配符类型
7.泛型的使用场景:
当类中要操作的引用数据类型不确定的时候,过去使用 Object 来完成扩
展,JDK 1.5后推荐使用泛型来完成扩展,同时保证安全性
8.泛型的总结:
1.源码中仍然有Object类型,是为了兼容,新代码中用泛型代替
2.泛型是通过擦除来实现的。因此泛型只在编译时强化它的类型信息
3.如果类型参数在方法声明中只出现一次,可以用通配符代替它
4.数组中不能使用泛型:array不能保证编译时类型安全
5.Java 中 List<Object> 和原始类型 List 之间的区别?
在编译时编译器不会对原始类型进行类型安全检查,却会对带参数的
类型进行检查
通过使用 Object 作为类型,可以告知编译器该方法可以接受任何类
型的对象,比如String 或 Integer
你可以把任何带参数的类型传递给原始类型 List,但却不能把 List<
String> 传递给接受 List< Object> 的方法,因为泛型的不可变
性,会产生编译错误。
7.异常
1.Throwable:Throwable 指定代码中可用异常传播机制通过 Java 应用程序
传输的任何问题的共性,Exception(异常)和 Error(错误)都是它的
子类
Exception(异常):是程序本身可以处理的异常。
Error(错误):是程序无法处理的错误,表示运行应用程序中较严重问
题。大多数错误与代码编写者执行的操作无关,而表示代码运行时
JVM(Java 虚拟机)出现的问题。例如,Java虚拟机运行错误(Virtual
MachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出
现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会
选择线程终止。
2.异常的分类:
可查异常:除了RuntimeException及其子类以外,其他的Exception类及
其子类都属于可查异常。这种异常的特点是Java编译器会检查它。发生
这类异常编译不会通过
不可查异常:运行时异常(RuntimeException与其子类)和错误
(Error)
3.处理异常的机制:
1.抛出异常:当一个方法出现错误引发异常时,方法创建异常对象并交付
运行时系统,异常对象中包含了异常类型和异常出现时的程序状态等异
常信息。运行时系统负责寻找处置异常的代码并执行
2.捕获异常: 在方法抛出异常之后,运行时系统将转为寻找合适的异常
处理器(exception handler)。潜在的异常处理器是异常发生时依次存
留在调用栈中的方法的集合。当异常处理器所能处理的异常类型与方法
抛出的异常类型相符时,即为合适 的异常处理器。运行时系统从发生异
常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理
器的方法并执行。当运行时系统遍历调用栈而未找到合适 的异常处理
器,则运行时系统终止。同时,意味着Java程序的终止。
3.一个方法所能捕捉的异常,一定是Java代码在某处所抛出的异常。简
单地说,异常总是先被抛出,后被捕捉的。
4.try-catch语句:一旦某个catch捕获到匹配的异常类型,将进入异常处
理代码。一经处理结束,就意味着整个try-catch语句结束。其他的catch
子句不再有匹配和捕获异常类型的机会
5.try-catch-finally语句
try 块:用于捕获异常。其后可接零个或多个catch块,如果没有catch
块,则必须跟一个finally块。
catch 块:用于处理try捕获到的异常。若无匹配抛给JVM处理
finally 块:无论是否捕获或处理异常,finally块里的语句都会被执行。当
在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被
执行。在以下4种特殊情况下,finally块不会被执行:
1)在finally语句块中发生了异常。
2)在前面的代码中用了System.exit()退出程序。
3)程序所在的线程死亡。
4)关闭CPU。
6.throws语句:
- 如果是不可查异常(unchecked exception),即Error、
RuntimeException或它们的子类,那么可以不使用throws关键字来声
明要抛出的异常,编译仍能顺利通过,但在运行时会被系统抛出。
2)必须声明方法可抛出的任何可查异常(checked exception)。即
如果一个方法可能出现受可查异常,要么用try-catch语句捕获,要么
用throws子句声明将它抛出,否则会导致编译错误
3)仅当抛出了异常,该方法的调用者才必须处理或者重新抛出该异
常。当方法的调用者无力处理该异常的时候,应该继续抛出,而不是
囫囵吞枣。
4)调用方法必须遵循任何可查异常的处理和声明规则。若覆盖一个
方法,则不能声明与覆盖方法不同的异常。声明的任何异常必须是被
覆盖方法所声明异常的同类或子
7.异常链:Java方法抛出的可查异常将依据调用栈、沿着方法调用的层
次结构一直传递到具备处理能力的调用方法
8.Throwable类中的常用方法:
getCause():返回抛出异常的原因。如果 cause 不存在或未知,则
返回 null。
getMeage():返回异常的消息信息。
printStackTrace():对象的堆栈跟踪输出至错误输出流,作为字段
System.err 的值。
9.常见的异常:
- runtimeException子类:
1、java.lang.ArrayIndexOutOfBoundsException数组索引越界异
常。当对数组的索引值为负数或大于等于数组大小时抛出。
2、java.lang.ArithmeticException算术条件异常。譬如:整数除零
等。
3、java.lang.NullPointerException空指针异常。当应用试图在要
求使用对象的地方使用了null时,抛出该异常。譬如:调用null对象的
实例方法、访问null对象的属性、计算null对象的长度、使用throw语
句抛出null等等
4、java.lang.ClassNotFoundException找不到类异常。当应用试
图根据字符串形式的类名构造类,而在遍历CLASSPAH之后找不到
对应名称的class文件时,抛出该异常。
5、java.lang.NegativeArraySizeException 数组长度为负异常
6、java.lang.ArrayStoreException 数组中包含不兼容的值抛出的
异常
7、java.lang.SecurityException 安全性异常
8、java.lang.IllegalArgumentException 非法参数异常
2.IOException
IOException:操作输入流和输出流时可能出现的异常。
EOFException 文件已结束异常
FileNotFoundException 文件未找到异常 - 其他
ClassCastException 类型转换异常类
ArrayStoreException 数组中包含不兼容的值抛出的异常
SQLException 操作数据库异常类
NoSuchFieldException 字段未找到异常
NoSuchMethodException 方法未找到抛出的异常
NumberFormatException 字符串转换为数字抛出的异常
StringIndexOutOfBoundsException 字符串索引超出范围抛出的
异常
IllegalAccessException 不允许访问某类异常
InstantiationException 当应用程序试图使用Class类中的
newInstance()方法创建一个类的实例,而指定的类对象无法被实
例化时,抛出该异常
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/9373.html