Java初级学习
2022-08-15 17:45:45 22 举报
AI智能生成
java初级学习思维导图,内容非常全面,能够总览几乎所有的java初级基础内容,总主题数上千,内容完全是自己手写,一点一滴积累起来的,相信会对其他人有所帮助。 适用于,java初级学习,java入门,java扎实的基础,等。 通过该思维导图也能,检测知识的掌握程度,能够通过该导图背与掌握绝大多数内容,在面试时得心应手
作者其他创作
大纲/内容
容器******
泛型jdk1.5
参数化类型
数据类型作为参数传递
只能配置引用数据类型
<>定义泛型
泛型的行为发生在编译期间,
运行期间泛型配置的所有内容无效,泛型擦除
要求先定义泛型,才能使用泛型
泛型类
类型的后面定义泛型,在使用类型时可通过泛型传递具体类型
类中可以进行使用泛型方法
使用
后边可省略<String>成<>或不写
但标准写法是ArrayList<String> list = new ArrayList<>()
泛型的优点
代码简单简洁
增强程序健壮性,避免类型转换异常的出现
增强稳定性与可读性
数组与集合的区别
集合的特点:灵活,根据需求增删改查
只能存储引用数据类型的数据,
所有引用数据类型的祖宗类是object
能够存储不同的数据
数组的特点:是引用类型,可以存对象
定长不可改变长度的序列
类型相同
有序,有索引
Collection接口
方法
添加 add()addAll()
删除 remove(),clear()
retainAll()求交集
toArray()
是否空 isEmpty()
contains(obj/col)
literator()
记数 size()
Collection的遍历
foreach遍历,因为无法获得索引,所以得通过foreach来遍历
迭代器遍历
通过iterator()方法获得迭代器
hasNext()方法判断是否有下一个对象
next()方法来获得对象
迭代器遍历该接口实例化的时候,
不可以对容器中的元素进行修改,否则报错
List接口(索引)
list
该接口的实现类可以精确控制列表中每个元素的插入位置
可通过整数索引(即元素在列表中的位置)来访问元素,来搜索列表中的元素
有序集合(也成为序列)——即有序的,可重复
list新增方法
add(index,E)(E),addAll(collection),copyof()
remove(index)object()
set(index,e),subList(fromIndex,toIndex)
get(),indexOf(),lastIndexOf()
,static of()jdk9以上
listIterator()
List的遍历
for循环
foreach
iterator迭代器
foreach和iterator迭代器在迭代期间增删,会产生异常,因为是两条线程在操作集合,导致数据不一致
listIterator列表迭代器
列表的迭代器,允许在任一方向上遍历列表,
在迭代期间修改列表,并获取迭代器在列表中的位置
列表迭代器有add()方法,可以在迭代时向List中添加对象
找到立即在当前位置添加
列表迭代器有hasPrevious()方法与previous()方法,可以实现倒序遍历
列表迭代器有nextIndex()方法与previousIndex()方法,
可以实现在迭代时获取迭代器在列表中的位置
列表迭代器有set()方法,可以实现在迭代时对对象的修改
ArrayList*****
存储的每个元素为单个值,有序,可重复,可以根据索引进行操作线程不安全
底层结构
动态数组 Object[] elementData
特点
根据索引查询效率高
做增删涉及到新数组的创建,以及原数组的数据拷贝,效率低
新增内容
foreach()流式编程
遍历
与List接口相同,即四种遍历方式
注意
容器初始容量:10
扩容机制:扩容原容量的1.5倍
存储引用数据类型时,需要在对象类型中重写equals,在比较时contains或indexof比较内部调用equals方法
适用场景:大量做查询,少量做增删的时候适合使用
Vector
向量
Vector与ArrayList的区别
共性
都是List接口下的实现类,都有序可重复
底层结构都是数组
与ArrayList的区别
线程安全问题 | 同步问题
Vector是同步的
ArrayList线程不安全,不同步
扩容机制
ArrayList与Vector初始容量默认都是10
ArrayList扩容:1.5倍
Vector扩容:2倍(可以设置每次扩容的容量)
StringBuilder和StringBuffer是2倍+2,初始为16
面试题:
LinkedList与ArrayList的共同点与区别
共同点:都实现的List接口,具有List的特点,有序,可重复,可以有多个null值,可以根据索引来操作,都是线程不安全的集合。
区别:
ArrayList底层为动态数组实现,具有数组的特点,增删慢,查询快,具有索引,可以根据索引快速查询到数据,扩容机制:一开始为0,在使用add时赋初值10,以1.5倍原长扩充
LinkedList底层为双向循环链表实现,具有链表的特点,增删快,查询慢,主要关注头尾节点,扩容机制:初始容量为0,以增加节点方式扩容
LinkedList
特点
有序,可重复
查询效率低,增删效率高
线程不安全
底层结构
双向循环链表
双链表,实现所有可选列表操作,并允许所有元素(包括null)
额外方法(头尾)
void addFirst(E e) 向此列表的开头插入指定的元素
void addLast(E e) 将指定的元素追加到此列表的末尾
E getFirst() 返回此列表中的第一个元素
E getLast() 返回此列表中的最后一个元素
E removeFrist() 从此列表中删除并返回第一个元素
E removeLast() 从此列表中删除并返回最后一个元素
E pop() 弹出此列表所代表的堆栈中的元素(等效于removeFirst())
void push(E e) 将元素推送到此列表所表示的堆栈上(等效于addFirst())
遍历方式
与List接口相同,四种遍历方式
适用场景
适用于大量做增删,少量做查询的情况
Set接口
继承自Collection接口,Set接口中无特殊新增方法,方法与Collection保持完全一致
特点
无序,不可重复
遍历
foreach
iterator迭代器
注意
无序即添加的数据与内部真实存储的顺序不一致
HashSet
是简化版HashMap
底层结构为:
底层结构
哈希表(jdk1.8之后为数组+链表+红黑树,jdk1.7之前为数组+链表)
HashSet的底层逻辑:
jdk1.7以及之前,数组+链表
元素过多,查询效率低。
创建了一个默认长度为16,默认加载因子为0.75的数组,数组名为table
根据元素的哈希值和数组的长度计算出存入的位置(索引)值
判断当前位置是否为null,如果是null,则直接存入
如果应存入的位置不是null,表示该位置有元素,则调用equals方法比较属性值
如果属性值一样则不存入,如果属性值不一样则存入数组,老元素挂载在新元素下面,形成链表结构。
每次存入一个数据都和链表中的元素进行equals比较属性,属性值一样,则不存,不一样则存,重复以上操作
当存满了16个长度的元素怎么办呢?
这是一个问题,这个问题就交给了加载因子,我们默认的加载因子是0.75。
当数组中存满了 16*0.75 = 12 个元素的时候,数组就会自动扩容为当前数组长度的两倍
HashSet的底层逻辑:
jdk1.8之后,数组+链表+红黑树
效率得到了很大提升。
若我们这个链表上挂载了很多元素,每一次进行存储都会在链表中一个一个去比较,
对判断的效率影响太大。故在jdk 1.8 中采用了 红黑树的结构
即当链表的长度超过8的时候,自动转化成了红黑树。
小的元素在右边,大的元素在左边,这样进行查询判断的效率就提高了
小于6则自动转为链表
黑红树:自平衡二叉搜索树(左右子树高差有可能大于 1)
二叉搜索树又称二叉排序树,二叉查找树,就是中序遍历,是从小到大排列的
特点
无序,去重
查询与增删效率比较高
允许有null元素
线程不安全
hashset使用对象类型来计算hashcode值,hashmap使用键值来计算hashcode值
遍历方式
foreach
iterator迭代器
注意
如果HashSet存储引用类型对象的时候,要在该类型对应的类中重写HashCode方法与equals方法,
不然会报错,无法对容器中的元素进行默认排序
TreeSet
底层
底层由TreeMap实现(本质也是一个简化版的TreeMap)
底层结构为红黑树(平衡二叉树)会自动进行平衡,左右子树深度相差超出一定限度
特点
TreeSet内部需要对存储的元素进行排序,默认为升序排序
也可以通过自定的排序规则来实现排序
默认情况下不允许添加null元素
应用场景
去重,对数据进行自动升序排序
新增方法
ceiling返回大于等于钙元素的最小数据,
没有返回null
floor
first()返回最小数据,last()
pollfirst。polllast()
higher(),lower()
subset(from,to)
遍历方式
foreach
iterator迭代器
比较器
内部比较器(自然排序):
通过元素对应类实现Comparable<T>接口,并重写compareTo方法来完成比较规则this.age-o.age,正序,this在后则返序
外部比较器(定制排序):
或者通过创建一个比较器(可以匿名内部类,可以单独创建一个类)。
lambda表达式,非常简化
比较器需要实现Comparator接口,并重写compare方法来定义比较规则。
哈希表
底层结构
节点数组+单向链表
初始容量
16
加载因子
0.75(一般不会修改)
扩容阈值(扩容临界值)
当前容量*加载因子,即存储的数据个数>当前容量*加载因子就会扩容
扩容机制
扩容为原容量的2倍
Map接口
Map与Collection的区别
1 Collection容器中的元素是孤立的,即Collection容器是单例容器
2 Map容器中的元素是成对存在的,可以通过键来找到对应的值。即Map容器是双例容器
特点
Map中的key是无序的,去重的 ----->类似于Set集合
Map中的value是无序的,可重复的-------->类似于Collection集合
遍历方式
keySet(),values(),EntrySet()
foreach
将所有的key值获取并放入一个Set集合中,然后通过foreach遍历
将所有的value值获取并放入一个Collection集合中,然后通过foreach遍历
将所有的key与value获取并放入一个Entry容器中,然后foreach遍历
iterator迭代器
将所有的key值获取并放入一个Set集合中,然后通过迭代器遍历
将所有的value值获取并放入一个Collection集合中,然后通过迭代器遍历
将所有的key与value获取并放入一个Entry容器中,然后迭代器遍历
常用方法
V put(K key,V value) 将指定的值与此映射中的指定键相关联
void putAll(Map m) 将指定映射中的所有映射复制到此映射
V remove(Object Key) 如果存在,则从该映射中移除键的映射
void clear() 从此映射中删除所有映射
V get(Object V) 返回指定键映射到的值,如果此映射不包含键的映射,则返回 null
boolean containsKey(Object key) 如果此映射包含指定键的映射,则返回 true
boolean containsValue(Object value) 如果此映射将一个或多个键映射到指定值,则返回 true
Set keySet() 返回此映射中包含的键的Set视图
Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射的Set视图
TreeMap
底层结构
红黑树
特点
key值不允许为null,value值允许为null
比较器
key默认升序排序,元素自身实现Comparable接口并重写了compareTo方法,则根据compareTo方法中的逻辑排序也可以在创建TreeMap对象的时候传入Comparator接口的匿名内部类,或者单独写一个类实现Comparato接口,重写compare方法,则根据compare方法中的逻辑进行排序
容器中key的元素,如果实现了Comparable接口,并重写了compareTo方法,可以实现去重(如果compareTo方法返回值为0,则认为重复)测试代码:如果创建TreeMap 的时候没有传入 Comparator ,且存入 TreeMap 中的元素没有实现 Comparable 接口,当调用 put 方法的时候,就会抛出异常 ClassCastException
线程不安全
应用场景
存储键值对数据的时候,key的值需要完成某种排序规则,便可以使用TreeMap
遍历
三种:values() map.of()
keySet() map.keySet()
entrySet()返回Map.Entry getKey(),getValue()
注意
TreeMap存储过程中,key的排序,去重都是根据比较器决定的
TreeMap线程不安全
HashMap与TreeMap的区别
HashMap比TreeMap效率高
TreeMap是可以对key进行排序的容器,默认升序,也可以通过比较器来完成对应的排序操作
HashMap*****
底层结构
底层结构
哈希表(jdk1.8之后为数组+链表+红黑树,jdk1.7之前为数组+链表)
jdk1.7以及之前,数组+链表
元素过多,查询效率低。
创建了一个默认长度为16,默认加载因子为0.75的数组,数组名为table
根据元素的哈希值和数组的长度计算出存入的位置(索引)值
判断当前位置是否为null,如果是null,则直接存入
如果应存入的位置不是null,表示该位置有元素,则调用equals方法比较属性值
如果属性值一样则不存入,如果属性值不一样则存入数组,老元素挂载在新元素下面,形成链表结构。
每次存入一个数据都和链表中的元素进行equals比较属性,属性值一样,则不存,不一样则存,重复以上操作
当存满了16个长度的元素怎么办呢?
这是一个问题,这个问题就交给了加载因子,我们默认的加载因子是0.75。
当数组中存满了 16*0.75 = 12 个元素的时候,数组就会自动扩容为当前数组长度的两倍
jdk1.8之后,数组+链表+红黑树
效率得到了很大提升。
若我们这个链表上挂载了很多元素,每一次进行存储都会在链表中一个一个去比较,
对判断的效率影响太大。故在jdk 1.8 中采用了 红黑树的结构
即当链表的长度超过8的时候,自动转化成了红黑树。
小的元素在右边,大的元素在左边,这样进行查询判断的效率就提高了
小于6则自动转为链表
黑红树:自平衡二叉搜索树(左右子树高差有可能大于 1)
二叉搜索树又称二叉排序树,二叉查找树,就是中序遍历,是从小到大排列的
分支主题
分支主题
特点
无序,key不可以重复,value可以重复
相比于Map接口,无新增方法
允许key为null,允许value为null
线程不安全
查找,删除,修改的效率高
应用场景,对增删数据与查询数据都有要求的键值映射数据,且去重的场景
遍历三种方式,同map
HashMap与Hashtable
共同点
都是Map接口的实现类,底层结构都是哈希表
增删查询都快,无序,key不可以重复,value可以重复
不同点
继承体系不同
Hashtable继承于Dictionary抽象类
HashMap继承于AbstractSet抽象类
线程安全方面不同
HashMap 线程不安全 | 不同步
Hashtable 线程安全 | 同步
扩容机制不同
HashMap扩容机制
初始容量为16,加载因子0.75,每次扩容为原容量的2倍
Hashtable扩容机制
初始容量为11,加载因子0.75,原容量的2倍+1
键值对数据null值得要求不同
HashMap 可以存储null值的key与value
Hashtable key与value都不允许为null
计算hash值与位桶索引index的算法不同
HashMap
int hash = (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);int index = (n - 1) & hash
Hashtable
int hash = key.hashCode();int index = (hash & 0x7FFFFFFF) % tab.length;
如何处理HashMap线程不安全问题
1 使用Hashtable
2 使用Collections工具类中static <K,V> Map<K,V> sychronizedMap(Map<K,V> m) 返回由指定映射支持的同步(线程安全) 映射
juc高级并发编程包ConcurrentHashMap<K,V> 线程安全的哈希表
Collections
操作容器的工具类 静态工厂类似于Arrays
void sort(List) //对List容器内的元素排序,排序的规则是按照升序进行排序
void shuffle(List) //对List容器内的元素进行随机排列
void reverse(List) //对List容器内的元素进行逆续排列
void fill(List, Object) //用一个特定的对象重写整个List容器
int binarySearch(List, Object)//对于顺序的List容器,采用折半查找的方法查找特定对象
Properties
Properties类表示一组持久的属性(键值对)。 Properties可以保存到流中或从流中加载。属性列表中的每个键及其对应的值都是一个字符串可以通过Properties实现软编码,便于后期维护
从配置文件中读取键值对数据
1 定义配置文件xxx.properties
2 定义Properties对象
3 调用load(),与从流中加载数据
Thread.currentThread().
getContextClassLoader().getResourceAsStream("db.properties")
4 根据key 获取value--->getProperty
作用,方便快捷安全,稳定
多线程***
概念***
进程与线程之间的区别***
进程
系统中的程序,系统中资源分配的最小单位,
每个进程都有自己的代码与数据空间,进程之间的切换开销较大
一个进程之间可以包含1~n个线程,
线程
程序中的顺序流,线程是cpu执行与调度的最小单位,
每一个线程都有自己的程序计数器运行栈,线程之间切换开销较小
多个线程之间共享进程的代码与数据空间,
分支主题
优点:提高性能、提高效率,资源利用率高
缺点:设计会更复杂,很可能出现数据不安全的情况
注意有的多线程是模拟出来的,真正的多线程是指由多个cpu,即多核,如服务器。如果是模拟出的多线程,即一个cpu,那么在同一时间点,cpu只能执行一个线程,因为切换快,所以有同时执行的效果
一个cpu同一时刻只能调度一个线程
三高:高并发,高可靠,高性能
创建线程的方式*****
1 继承Thread,重写run()方法+start()方法开启线程
不灵活,单继承
2 实现Runnable接口,重写run()方法+new Thread(new 对象).start()方法开启线程
优点:
接口多实现,更灵活,实现资源共享
但返回值,异常抛出类型要小于等于原重写方法,修饰符大于等于原修饰符
静态代理模式
3 实现Callable接口,重写call方法+线程池
ExecutorService server=Executor静态工厂.newFixedThreadPool(99)
Future<Integerserver.submit(对象)
integer sum=result.get()
server.shutdown()
优点:
call()方法可以抛出异常,可以定义返回值,而run()方法不可以,但更复杂
还可以使用静态内部类,成员内部类,局部内部类,匿名内部类和lambda表达式来简化使用
线程的状态**
新生状态
new Thread()
就绪状态
线程进入到就绪状态
线程可以被cpu调度
1 start()方法
2 阻塞解除
3 cpu的调度切换
4 yield 礼让线程
当线程执行到yield()方法时,会让出cpu的资源,同时线程会恢复到就绪状态
运行状态
当cpu调度一个处于就绪状态的线程时,这个线程会进入到运行状态
阻塞状态
一个线程如果进入到阻塞状态,阻塞解除之后,不能直接恢复到运行,会恢复到就绪状态,等待cpu的调度
如何让线程进入到阻塞状态
1 sleep()方法
sleep线程休眠
static void sleep(long millis) 导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,
具体取决于系统计时器和调度程序的精度和准确性
static void sleep(long millis, int nanos) 执行ms+ns
当一个线程调度sleep()方法进入睡眠状态,就会让出占用cpu的资源
抱着资源睡觉:这个资源不是cpu的资源,指的是对象的锁资源
作用:
放大问题出现的可能性
模拟网络延迟
2 join()方法
join()方法 插队线程
A线程执行过程中,如果B线程插队了,A线程就会进入到阻塞状态
,等待插队线程执行完毕 | 等待执行的时间,A线程会恢复到就绪状态
void join() 等待这个线程死亡
void join(long millis) 此线程最多等待 millis毫秒
void join(long millis, int nanos) 此线程最多等待 millis毫秒加上 nanos纳秒
3 wait()方法
等待时间或指定线程来唤醒
4 IO
终止状态
一个线程一旦进入终止状态,便不可恢复
如何让线程进入终止状态
1 正常执行完毕
2 stop() 已过时,不推荐使用
3 通过标识判断
getState(),返回为Thread.State 里面的枚举
中断机制
中断标识默认是false,被中断一次后就会变成true
void interrupt() 为线程添加一个中断标识
boolean isInterrupted() 测试此线程是否已被中断,即测试是否曾经调用过interrupt方法添加了中断标识
static boolean interrupted() 测试当前线程是否已被中断,是否曾经调用过interrupt方法添加了中断标识,同时复位标识
注意:当调用sleep()方法导致线程休眠时,如果有任何线程中断了当前线程就会报错。
InterruptedException 抛出此异常时,将清除当前线程的中断状态
线程优先级
1-10,10是最大,5是默认值,1是最小,超过范围报错非法字符,只能放大优先级执行的概率,不是顺序
方法
getPriority()
setPriority()设置优先级
MAX_PRIORITY
MIN_PRIORITY
NORM_PRIORITY默认
isDaemon()是否是守护线程
setDaemon(boolean)将线程标记为用户或守护线程
先设置守护再start()运行
线程分类
分为用户线程与守护线程,用户线程结束守护线程跟着结束
垃圾回收机制就是典型的守护进程
线程安全*****
synchronized
概念:多线程同时操作同一份资源时,可能出现线程安全问题
同步锁:让多个线程排队执行:重点是代码的范围,达到数据安全
组成:1.同步代码的范围(排队执行代码)
2.同步的条件:对象锁(一个对象只有一把锁,当锁没有释放,
其他线程无法获取),线程等待是阻塞状态
同步方式 同步方法:把锁放到方法上
静态方法:锁类的class对象
成员方法:锁对象,锁this
同步代码范围是整个方法体
同步块:synchronized(){}
()内锁类.class,锁对象,锁资源
注:每个类只有一个class对象,唯一的在第一次类加载到内存时,就存在,不用创建,只能获取
同步方法优缺点:简单,但效率低,一些代码可能不需要同步
同步代码块优缺点:效率块,但复杂,按需锁,范围不对可能锁不住
死锁(理解)
很多死锁都是因为逻辑问题出现的
线程通信
线程之间互相通信
方法实现
wait(),notify(),notifyAll()等方法,来自object类所有的子类都可以调用
wait()
等待,当执行到对象,当前线程会进入到与该对象相关的等待池中等待(等待队列或阻塞)
等待指定时间|等待被唤醒会释放cpu资源,同时释放对象锁资源
sleep()
休眠,抱着锁资源睡觉,只让出cpu资源
notify()
唤醒,将等待池中的线程唤醒进入就绪状态,不会马上执行,等待cpu调度并获取对象资源
网络编程***
网络与网页编程区别
网络编程
底层数据传输
网页编程
上层应用
一些知识点
IP:定位互联网中的节点
端口:区分同一台电脑的不同软件
URL:统一资源定位符,万维网资源指针,
互联网三大基石:http。url,html
组成:协议,域名,端口,资源
协议;合同标准,规范
传输层协议
UDP
写信,邮递,协议更简单,开销小,效率高
非面向连接,数据不安全(丢失),大小限制,一般不超60K
数据传输平等,基于字节数组
面向socket编程
socket套接字:传输层为应用层开的口子,做传输层与应用层之间的传输
DatagramSocket:
send
receive
构造器等
DatagramPacket():
构造器
发送数据(指定IP,端口
接收数据(不指定
发送流程
1.定义发送端
2.准备数据包
3.发送数据
4.关闭
接收流程
1.定义发送端
2.准备包,用于接收数据
3.接收数据
5.关闭
TCP
打电话,面向连接,安全,效率低,大小无限制
基于三次握手,四次挥手
两端不平等,存在客户端与服务端概念
服务端会阻塞式监听,客户端请求并建立连接
基于Io操作传输数据
客户端流程
1定义客户端
2准备数据
3io流把数据发送到服务端
4刷出
5关闭
服务端流程
1定义服务端
2阻塞式监听
3获取输入流,读取数据
4处理数据
5关闭
java.net包
InetAddress(),ip地址,IPV6,16字节,128位,IPV4,4字节,32位
方法
getLocalHost()
getHostName()
getHostAddress()IP
getByName() 域名+IP
www.baidu.com
InetAddress(port)(hostname,port)
URL url =new URL()
url.getProtocol()
getHost
getPort
getPath
getFile等等
特殊IP
192.168.0.0~192.168.255.255:非注册地址,供组织内部使用的IP
127.0.0.1(localhost):本地IP
域名解析:本地解析,服务器解析
端口
区分节点中的软件
2个字节的范围:0~65535
同一个协议下端口不能冲突,建议8000以内端口不使用,预留端口号
80:http
8080:tomcat
1521:oracle
3306:mysql
多用户登录
1.排队登录,循环结束再能下一次循环
2.多线程,多客户登录
Stream
区别
IO:传输数据
Stream流: 操作数据,计算数据,数据的运算,流式编程,流式运算,链式编程
对数据源产生的元素序列进行运算
数组 | 集合:存储数据
特点
1 Stream流本身不会存储数据
2 Stream不会修改数据源 | 源对象,每次会返回持有结果的新的流Stream,继续运算,不断运算
3 延迟执行 | 惰性加载 : 当获取终止行为时候,才会执行一系列的中间操作
4 流都是一次性的流,不能重复使用多次,一旦使用过就已经被破坏
lambda表达式
函数式接口
四大内置函数式接口
消费型Consumer<T>:accept(T)
函数型Function<T,R>:apply(T)
断言型Predicate<T>:test(T)
供给型Supplier<T>:get()
Bi两个参数传入,binary是参返一致
DoubleFunction
Int。。。
匿名内部类使用外部成员,此成员默认会变为静态常量
java.util.function包提供了一系列的函数式接口供选择使用
方法引用
格式
类名::静态方法名
对象::成员方法名
条件一:1.lambda体是否是调用另外一个方法实现的
2.内部所引用的方法的参数列表与返回值是否与lambda表达式参数列表和返回值一致
类名::成员方法名
条件二1.lambda体是否是调用另外一个方法实现的
2.内部所引用的方法的参数列表与返回值是否与lambda表达式参数列表和返回值一致,lambda第一个参数作为内部调用方法的对象存在,第二个参数后一一对应
当lambda体({})的实现,是通过调用另外一个方法实现的时候,考虑是否满足方法引用要求,可以通过方法引用直接引用那个方法简化lambda
步骤
1 创建Stream
1)Collection ->stream
list.stream()
2)Arrays ->stream(数组)
Arrays.Stream(arr)
3)Stream.of(值列表)
2 一系列流式的中间操作(都会返回一个持有结果的新的流)
1 过滤 Sream filter(Predicate<? super T> predicate)
2 去重 distinct() (比较equals()与hashCode())
3 截取 limit(long n) 从第一个开始截取几个
4 跳过 skip(long n)跳过前n个
5 排序 sorted() ->内部比较器 sorted(Comparator) ->外部比较器
6 映射 map(Function fun) stream操作的每一个数据都所用于参数函数,
映射成一个新的结果,最后返回一个持有所有映射后的新的结果的流
7.faltMap,流中流,将所有个流汇成一个新流并返回,将二维数组转为一位数组
3 终止行为
1 遍历 foreach(Consumer)
2 查找与匹配
allMatch() 检查是否匹配所有元素
anyMatch() 检查是否至少匹配一个元素
noneMatch() 检查是否没有匹配所有元素
findFirst() 返回第一个元素
findAny() 返回当前流中的任意元素
count() 返回流中元素的总个数
max() 返回流中最大值
min() 返回流中最小值
3 规约 reduce
map ->reduce加工 ->计算结果
4 收集
collect()
collect(Collector<? super T,A,R> collector) 使用 Collector对此流的元素执行 mutable reduction操作
Collectors的方法分为两类
一类是对Collector中的数据进行操作或者计算
counting()方法(计算数据个数)或
averagingDouble(ToDoubleFunction<? super T> mapper)方法(计算容器中数据的平均值)
一类是将Collector中的数据另存到一个容器中
存到List中则调用toList()方法
存到Set中则调用toSet()方法
存到Map中则调用toMap()方法
conllectors.toMap
因为Map对象中的key无法重复,放入该Map对象的key数据也不可以重复,
如果重复就会报错IllegalStateException,即要保证转为Map对象的时候key值不可以重复
(如果是平时的Map对象,需要调用put()方法来向容器中添加元素,
但是这里是通过toMap()方法直接将该容器转为Map容器,所以key的数据不可以重复)
还有counting,mapping,maxbyminby,filltreing等等
其他
反射
定义
开源框架常用,reflection,在运行中,对任意一个类都能知道该类的所有方法与属性(发生在程序运行期间),java中唯一一个动态加载机制
作用
java反射机制内容:(功能)
1.在运行时判断任意一个对象所属的类;
2.在运行时构造任意一个类的对象
3.在运行时判断任意一个类所具有的成员变量与方法
4.在运行时调用任意一个类的方法
5.动态代理生成
内容
反射的源头:class类实例表示正在运行的java应用程序的类与接口
注意:class对象在类加载到内存时就会存在,唯一只有一个,包含类中所有的内容,只要获取class对象,就可以操作类中的内容
使用
获取class对象:范反射源头
1.类名.class
2.class.forName(权限定类"包名.类名")-->推荐_>更灵活
3.对象.getClass()
4.getSuperClass()
5.基本数据类型.class==Integer.Type
能获取构造器,注解,接口等等供框架使用,方法修饰符,数组,枚举,注解,基本数据类型等
反射操作构造器|创建对象
1.newInstance()默认调用空构造,在创建对象的同时对对象,初始化信息---》已过时
2.先获取构造器,然后指定在创建对象时使用哪一个构造器
constructor:
getConstructor()
getConstructors()
以上获取公共构造函数
getDeclaredConstructor()
getDeclaredConstructors()
获取所有构造函数
使用:私有构造器要先忽略权限
con.setAccessible(true)
con.newInstance()
con.setAccessible(false)
反射操作属性
getField()
getDeclaredField()
Field.get,set,equals等
反射操作方法
1.获取方法
getDeclaredMethods
getMethods
2.使用方法
invoke(对象,参数)
或invoke(null(指的是类),参数)
注解
xml
正则表达式
设计模式
某些对象的创建是比较耗时的,并且很多情况下可能这些对象是可以复用的。所以希望某个类创建对象的数量只有一个(单例),或者是恒定数量的(多例)。设计模式(23种)
开闭原则
对维护的代码关闭修改,允许扩展代码
目的:防止代码出现问题,导致业务功能无法使用
单例模式
单个实例叫做单例。在业务中,需要当前类有且只能创建一个对象。此时,我们可以通过单例模式实现。单例模式的实现方式有5种。设计模式是不单独出现的
饿汉式单例
优点:天然线程安全
缺点:不能延迟加载
Tips:如果该种单例类种存在一个其他的静态方法,外界调用该方法会导致该种单例类被加载到内存中,而加载过程又会触发static加载(有且只做一次),静态方法进内存之后,等待调用,静态属性进内存之后,会触发赋值操作(如果没有初始化,赋值为默认值,如果初始化了,则将初始化的值赋给静态变量)。即只要调用其他静态方法,无论是否需要这个单例类对象,都会在内存中自动加载一个该类的对象。所以缺点就是占用一定内存资源。
懒汉式单例
特点:支持延迟加载,但线程不安全
模板方法
分支主题
策略模式
分支主题
数据库
Java预备知识
Java的跨平台(write once run anywhere)
分支主题
Java如何解决慢的问题
产生原因:Java传统上而言,是一门编译型语言
解决方法:采用JIT与AOT来提升整体运行速度
实时编译和预编译
Dos命令寻址
操作系统
windows
自带图形化界面
自带命令行界面DOS
Mac
自带图形化界面
自带命令行界面DOS
Linux
图形化界面
蝶变:UBT
命令行界面终端
RedHat:CentorOs
环境变量逻辑
分支主题
Java的基础规则:
一个文件中可以有多个类,但只能有一个类被public修饰符修饰,称为主类。若一个文件中有两个类,则编译的时候会有两个字节码文件
类名即为文件名
一个类中只能有一个main方法
***Java基础知识
基础内容
其他基础
标识符
三个准则
Java要求不能以数字开头,以字母,下划线,美元符开头,后跟字母,美元符,下划线,数字结尾
Java中的标识符不能为关键字
Java严格区分大小写
两个标准
驼峰原则
见名知意
关键字
字符集合
注释
变量
变量名称
变量类型
Java是一门强类型语言
对于变量而言存在数据类型,对于数据本身而言也存在数据类型
作用范围
数据类型
数值型
Byte
数据类型:Byte 字节型
所占空间:1B
作用范围:[128,127](-2^7-2^7-1)
注意:赋值的时候,不能超过byte类型的范围,否则会报错:int转byte会有所损失
Short
数据类型:Short 短整型
所占空间:2B
作用范围:[-32768,32767](-2^15-2^15-1)
注意:赋值的时候,不能超过Short类型的范围,否则会报错:short转byte会有所损失
int
数据类型:int 整型
所占空间:4B
作用范围:(-2^31-2^31-1)
long
数据类型:long 长整型
所占空间:8B
作用范围:(-2^63-2^63-1)
注意:赋值的时候如果将一个超过了int范围的字面值赋予long类型的变量,需要在整个字面值后面加l或者L
float
数据类型:float 单精度
所占空间:4B
作用范围:小数点后6-7位,前6位绝对精准,7位部分精准
注意:把一个字面小数赋值给float变量的时候,需要在后面加F或者f
double
数据类型:double 双精度
所占空间:8B
作用范围:小数点后15-17位,前15位绝对精准,17位部分精准
注意:不可以用小数进行运算,可以通过扩大倍数计算再缩小到原位数来达到计算目的
数值型数据类型需注意:
在Java中所有的整数的字面值默认类型都是int类型
计算机中的小数是有穷尽的。在计算机中可以发生等概率事件的
所有的小数默认数值类型都是double类型
字符型
char
数据类型:char 字符型
所占空间:2B
取值:用单引号引起来,可表示65535个字符
表达方式:
char ch='a';(直接输入字符)
char ch='22';(输入在[0,65535]的整数)
char ch='\u56CE';(\u后加一个四位的十六进制)
a=97
A=63
字符的相加是ASCII码的相加,最后的结果默认是int除非用这样子的代码将原字符每个都转为字符串,否则就是字符串的相加
逻辑型
boolean
数据类型:逻辑型 boolean
所占空间:1字节(本来只需要1位的空间即可,但是由于java语言的要求,内存中开辟的空间必须是8的整数次幂,所以实际占用了8位即1字节的空间)
取值:true,false
注意:true和false是关键字
类型转换
自动转换类型 小变大 自动转 看所占空间 byte转short
强制类型转换 大变小 要强转 short转byte
自动类型转换细节
在四则运算中,自动向大类型靠拢
boolean类型不能和其他任意类型进行转换
运算符
分支主题
赋值运算符
使用赋值运算符时,是自右向左进行运算得
算术运算符
+号
表示正负,即为正号
四则运算中的一种,即为加法运算
当加号左右两边的操作数有一个是字符串类型的时候,此时+号是连接符号,会将两个操作数连接成为一个新的字符串
-,*,/,%
-号
表示正负,即为负号
四则运算中的一种,即为减法
* 即为乘法
/ 即为除法
% 即为取余数
注意:进行运算时,会自动向大类型靠拢
++和--
单独一条语句时,自增1或者自减1, ++或--放在前面或者后面都没有区别
当进行自增或自减的值会赋予其他变量值时会发生变化
++或--置于后面就会先赋值,后自增或自减
++或--置于前面就会先自增或者自减,再赋值
关系运算符
关系运算符最后的结果都是boolean类型
对于 < <= > >= 来说,结果是boolean类型,基本数据类型除了boolean 都可以比较,不可以比较引用类型(引用类型包括类,数组,接口)
对于 == !=来说,结果是boolean类型,所有的基本数据类型和引用类型都可以比较
instanceof 结果是boolean类型,比较引用类型,用来查看某个对象是否属于某个类型,或者查看类是否是另一个类的子类
逻辑运算符
逻辑运算符有:& && | || ! ^,其中最常用的且效率高的为: &&与 || !
&& 两个都为true,其结果为true 剩余的都是false
|| 两个都为false,其结果为false,其余的都为true
! 取反运算,true变为false,false变为true
短路与 和短路或效率高,因为进行判断时会先判断运算符前面的boolean值,如果不是对应要求的值,便直接返回false或者true
扩展运算符
扩展运算符有: +=,-=,*=,/=,&=,|=,>>=(算数+赋值 或者是位+赋值)
优缺点:
1 提升了编译效率
2 提升开发效率
3 自动做强转
4 降低了阅读体验感
条件运算符
表达式1? 表达式2: 表达式3
先计算表达式1的值,如果表达式1的值为true,该运算符的结果为表达式2的值,否则为表达式3的值
优先级问题
不要试图通过优先级来确定程序的执行顺序,要通过括号来确定执行顺序
有括号先算括号里面的
操作数越多 优先级越低
算数> 关系>逻辑>条件>扩展>赋值
流程控制
流程控制分类
顺序流程
分支结构
通过某些判断条件,程序执行的过程中,按照某个分支进行
循环结构
条件往复的情况下,往复的执行某一些代码
循环结构
while循环
语法结构
while(expression){// 表达式 boolean值 //loop-statment 循环语句}
执行顺序
判定表达式的值,如果表达式的值是true,执行循环体一次
继续判定表达式的值,直到表达式的值是false,则跳过整个while循环执行后续代码
注意事项
尽量防止写成死循环(死循环是有意义的)
do-while循环
语法结构
do{ //loop-statment}while(expression);
执行顺序
先执行循环体一次
再判断表达式的值,如果表达式的值是true执行循环体一次
直到表达式的值是false,则跳过整个while循环执行后续代码
注意事项
先做一次,然后再判定
for循环
语法结构
for(循环变量声明;循环条件;){ 循环体;}
执行顺序
先声明循环变量
再判断表达式的值,如果表达式的值是true执行循环体一次
执行i++操作
继续判定表达式的值,直到表达式的值是false 则跳过整个for循环执行后续代码
注意事项
while : 确定循环终止条件
for: 能够确定循环次数
分支结构
switch分支
语法结构
执行顺序
计算表达式的值
依次匹配对应的case 如果匹配上了之后 执行对应的执行语句,结束整个switch分支
如果所有的case都不满足 则执行最后的default
注意事项
switch表达式中能够产生的结果只能是:
char,int,byte,short,或者是对应的包装类
jdk1.5之后支持enum枚举类型
jdk1.7之后支持String类型
switch中的case穿透问题 需要通过break来防止穿透,也可以利用case穿透的特点来简化看法
if分支结构
单分支结构
语法结构
分支主题
执行顺序
判定表达式的值
如果表达式的值是true 则执行执行语句
如果表达式的值是false 则跳过if 执行后续代码
注意事项
单分支相当于只开了一条分支,在某些情况下可能不太够用
双分支结构
语法结构
分支主题
执行顺序
判定表达式的值
如果表达式的值是true 则执行执行语句1
如果表达式的值是false 则执行执行语句2
注意事项
双分支结构中执行语句一定是互斥的,所以一定会保证有一个是一定被执行的
多分支结构
语法结构
分支主题
执行顺序
判定表达式1的值
如果表达式1的值是true 则执行执行语句1 结束整个多分支
如果表达式1的值是fasle 判定表达式2的值 以此类推
如果所有的表达式都不满足 执行最后一个else对应的执行语句
注意事项
多分支结构中分支越多可能程序会变慢,多分支中分支过多会造成阅读体验感变差,维护性降低
流程嵌套和循环中断
break continue语句
continue语句
continue语句在循环句体中,用于终止某次循环过程,跳过循环体中尚未执行的语句,接着进行下一次是否执行循环判定的判定
break语句
在任何循环语句的主体部分,均可用break控制循环的流程。break用于强行终止整个循环,不执行循环中剩余的语句(break语句还可用于多支语句switch中)
注意:break用于终止整个循环
从跳出的程度上而言:
continue<break<return
continue:跳出一次
break:跳出本层循环
return:跳出方法
***方法
方法就是通过一组{} 将多行语句进行收集整理,完成功能的复用
变量的作用域
变量的分类
变量按照它声明的位置不同,将其分为局部变量和实例/成员变量
声明位置
一个变量声明在方法中或者是代码块中,将其称为局部变量(方法参数中的形参也算局部变量)
一个变量声明在类中,方法外,将其称为实例变量或者成员变量
实例变量
声明位置不同
作用范围不同:从声明位置开始一直到这个类结束而结束
存在默认值
整数型:默认值为0
小数型:默认值为0.0
char型:默认值为空格
boolean型:默认值为false
引用数据类型:默认值为null
局部变量
对于局部变量而言,它的作用范围是从声明的位置开始,
到第一个右侧的大括号(声明的位置向前一个tab键)结束
局部变量要想使用,必须要保证先声明、再初始化
在同一个作用范围下,不能出现同名局部变量
代码块
通过一组{}括起来的称之为代码块
局部代码块
定义在方法中的代码块叫做局部代码块应该合理的使用内存空间。降低内存使用率,目前而言用的比较少
初始化块
每次创建对象都只会执行一次,编译生成的字节码文件中会将初始代码块中的内容复制一份给每个构造器并都粘贴一份
为什么每次创建对象都只会执行一次初始化块:若构造方法1调用了构造方法2,则通过构造方法1创建对象的时候本来会调用两次构造方法,
即构造方法1和构造方法2,但由于只创建了一个对象,则初始化块只会执行一次
一道题目:
静态代码块
静态代码块只会被执行一次,即当前类被加载的时候会被触发
1 new当前类的实例对象
2 调用当前类的静态内容
3当前类是一个启动类(包含main方法的类)
使用的场景很多,本质上的原因就是因为他只会被执行一次
方法
方法定义
方法语法: 修饰符+返回值+方法名([参数列表]){方法体}
为什么需要使用方法
把所有的代码放到main方法中是不好的
1 不利于后期维护
2 不利于代码复用
3 不利于阅读 体验感很差
方法的声明和使用
在当前类中,和main方法同级
调用的时候通过方法名称调用
方法参数问题
在java中确定方法调用的方式:方法名称+参数列表(个数)
参数列表其实就是当前方法的局部变量,在方法调用的时候变量初始化,所以在方法中可以直接使用参数列表中的变量,
因为调用的时候会传入初始化的值(形参虽然看着没有初始化,但是方法被调用的时候形参也会被初始化)
返回值
一个功能完成之后,需要告知调用者最后的结果。这个结果就称之为返回值
方法重载
方法重载:2同3不同
2同:同类,同名
3不同:参数列表不同[个数,顺序,类型]
重载的面试题
重载调用满足的条件:最近最优
完全匹配>自动类型转换(看变量的转换次数)
分支主题
方法签名
方法签名由方法名+形参列表构成
方法签名的意义:方法名和形参数据类型列表可以唯一的确定一个方法,与方法的返回值一点关系都没有,这是判断重载重要依据
其他知识点
常量
程序运行期间其值不可以发生改变
常量分为
字面常量:2,12,22,'A'
符号常量:通过final修饰的变量称之为常量 常量在程序运行期间,值不可以发生改变
符号常量的标识符
全部大写,然后单词之间通过_分割 例:YOU_JI_XIAN_XUE_TANG
拓展:常量的编译速度很快
递归
在方法中,程序调用自身的编程技巧称之为递归
关于递归:运行效率慢,吃内存。一般在企业开发中用不到
循环的效率远高于递归
递归解决效率慢的方式:
将一些重复计算的中间结果 暂存起来
使用尾递归(不是所有的递归都可以写尾递归)
数组
数组是一个存储相同类型数据的,开辟连续的存储空间有序集合(一组数)
数组的初始化
静态初始化
第一种静态初始化
第二种静态初始化(一般不用)
动态初始化
分支主题
数组中的数据可以自动向上转型
内存情况
以后只要看到new关键词(栈中的对象名指向堆中的地址)
1 开空间,在堆内存中开辟一份空间出来,赋默认值
整数为0
小数为0.0
布尔值为false
char值为空格
引用型为null
2 构造器初始化 ,给数组的每个索引位置上的元素进行初始化
3指向引用,将堆内存中的地址赋值给局部变量进行存储
内存情况
基本数据类型存储在栈中
数组的常用属性
常用属性:索引、长度
只要学习存储数据的集合,就考虑这五种操作:增删改查+迭代
数组的迭代
数组的迭代即将数组中的元素 依次拿出来瞅一眼
通过普通for循环以及索引来达到迭代(这种方式的迭代可以对每个索引下的值进行操作)
通过增强for循环(foreach循环)来直接迭代
小拓展:二维数组
二维数组的声明
二维数组的初始化
数据类型[][] 数组名 = new 数据类型[外层二维的长度][内层一维长度];
内层每一个一维数组的长度都是固定的,外层二维数组与内层所有一位数组都已创建
数据类型[][] 数组名 = new 数据类型[外层二维的长度][];数组名[外层二维索引] = 一维数组创建方式;
外层二维数组已创建,内层的所有一维数组没有创建,在能够确定长度或者数据的时候再根据需求创建
数据类型[][] 数组名 = new 数据类型[][]{{1,2,3},{4,5},{6},...};
操作二维数组中的数据根据索引来操作:数组名[二维索引][一维索引]
二维数组的遍历:for或者foreach都可以遍历
数组的边角知识
关于main方法中的参数问题
main方法中的参数默认情况下,被java解释器调用之后,传入的应该是一个空数组对象。可以通过配置来传入具体的参数
java解释器 一定给args传参数
java解释器在执行main方法的时候传入了一个数组对象 这个对象默认情况下长度为0(不是该对象为null,如果该对象为null就不会在堆内存中开空间,即不可能获得长度)
但是该数组对象一定占用类内存空间(因为new了就要开空间)
可变参数问题
JDK1.5之后支持形式参数为可变参数
可变参数可以接受任意长度的固定数据类型的调用,但是必须要保证方法的参数列表中有且只能有一个可变参数并且这个可变参数一定在末尾
可变参数本质上就是一个数组,只不过编译器会自动帮助我们去构建该数组
传入可变参数中的实参个数可以为0个,可以为1个,可以为多个
有可变参数方法的声明:
面试问题
为什么数组的索引是从0开始的?请说明一下数组的优缺点。
请说明一下你们公司中哪些业务场景使用了数组
(这个问题面试的时候需要仔细询问具体环境,相当于面试中的一个坑)
1 我们可以通过计算的方式快速地定位指定索引上的元素地址。
(索引位置的地址=首地址+(索引)*数据长度)所以数组通过索引去获取元素效率是特别高的
2 数组的问题就是不能增加和删除。如果要添加和删除,
其实就是在新数组上操作的,根本无法在原数组上进行,所以效率特别差
数组最大的问题:数组的长度一旦指定无法修改
3 如果在使用场景中,大量的出现了添加和删除,不建议使用数组进行操作,
如果有高频次的查询操作,并且可以将业务中的模型和索引进行映射关系处理,那么优先使用数组
冒泡排序
可变参数
参数类型相同,参数个数可以从0到多个
是jdk1.5之后的特性
格式:数据类型... args 例如String ... args
注意点:方法参数要有多个参数要把可变参数放在后边
这样可限定至少求一个整数的和
java内部会为可变参数构件数组
但数组不能转为可变参数
Array工具类
copy()
都来自于System.arraycopy
(数组1,起始位置,数组2,起始位置,复制的长度)方法
在java.util.arrays中
static int[] copyOfRange(int[] original, int from, int to) 将指定数组的指定范围复制到新数组中。
static int[] copy(int[] original,int newLength):使用0复制指定的数组,截断或填充(如有必要),以使副本具有指定的长度。
fill()
static void fill(int[] a, int val),将指定的int值分配给指定的int数组的每个元素
static void fill(int[] a, int fromIndex, int toIndex, int val) 将指定的int值分配给指定的int数组的指定范围的每个元素。
static void sort(Object[] a)
默认对数组中的数据做升序排序,
如果A对应的类继承了Comparable并且重写了compareTo方法,那么sort排序将按照compareTo方法体中的逻辑进行排序
字符根据unicode
字符串的比较通过首字母比较和后续的字母通过Unicode比较
static int binarySearch(int[] a, int key) 使用二进制搜索算法在指定的int数组中搜索指定的值
该方法要求参数数组必须升序排序。
如果数据不存在,则返回 -插入点的索引-1
如果查询的数据比数组中的所有数据都小,那么返回值将会是-1即(-插入位置-1)=(-0-1)=-1
equals(byte[] a, int aFromIndex, int aToIndex, byte[] b, int bFromIndex, int bToIndex)方法,
或者
equals(byte[] a, byte[] a2)
先比长度,后比内容,左闭右开
toString()获取数组的内容,不是重写是重载方法
asList(T... a)方法:将数组转成列表
二维数组的内容或者俩数组比较
DeepToString()
DeepEquals()
DeepHashcode()
API的使用
大概了解,再次遇到时,像查字典一样去查找,使用
面向对象*******
语言的分类
面向过程:可以处理一些简单的事情,但是复杂的事情,通过面向过程就显得比较不舒服。适合处理结构化问题。通过先设计框架,然后拆分,一步步的去解决,然后组合起来来解决一个具体的问题
面向对象:做简单的事情就是大材小用,但是处理复杂的业务就更加的得心应手一点
面向过程和面向对象不是对立的
对象和类的关系
对象:具备一定能力的个体。在java中万事万物皆为对象
类:类是一个抽象的概念。类就是一个模板,由这个模板可以产生无数个对象普通类的修饰符有:public,default(即默认修饰符,不用写),abstract,final
先有类还是先有对象?
先有类 再有对象:编写代码角度,先有类,再有对象,对象是通过类产生的
先有对象再有类:构建角度,所有对象的共性勾勒出来,定义为一个类
构造器
构造器的作用:为对象初始化信息
构造器特点
方法名称和当前类是同名的
这个方法不需要编写返回值
这个方法内部不需要编写return语句
构造器每个类中会存在一个默认的构造器,这个默认的构造器是空参数的。
保证没有写构造器也能够创建当前类的对象
如果在一个类中添加了任何一个构造器之后,默认的空参构造器就不存在了。
所以我们一般情况下在编写构造器的时候,会写1个默认构造器
构造器是支持重载的
构造器的互相调用只能使用this() 不能通过方法名称进行调用
通过new才能创建对象,构造器返回一个对象只是感觉起来像是构造器返回的,本质上对象是new开创空间后产生的,构造器只是对这个对象进行一步一步加工(代码块中赋予属性初始值等等操作),最后依次传出构造器(如果构造器中有通过this()调用其他构造器的话,一个构造器产生的对象会传给另一个构造器,然后依次传出,直到new关键字调用的构造器结束),产生一个对象
this关键词
this. 当前对象的 谁调用我 我是谁
可省略
排除下面的都是可以省略的
不可省略
局部变量和成员变量同名的情况下不可以省略
this() 只能在构造器的首行调用其他的构造器。减少了重复编码,提升可用性
封装
分支主题
封装修饰符/结构
public
√
√
√
√
√
protected
√
√
√
√
×
默认的(default)
√
√
√
×
×
private
√
×
×
×
×
修饰符只能修饰成员,不能修饰局部,
只有public和default能修饰外部类
protected访问成员要求:
通过继承访问,new对象不可以
通过子类对象,父类对象不行
同包类
属性封装的意义
当前类中某些属性不想让别人看到,所以通过封装来达到隐藏的目的(隐藏的效果根据条件来判断)
可以提高安全性和可复用性
某些属性的获取和设置相对而言比较简单,并且属性的值没有做一些限制,导致某些属性在赋值过程中出现赋值问题无法发觉;
即修改属性值的时候可以做一些权限甄别与条件判断工作
构造器封装的意义
某些对象的创建是比较耗时的,并且很多情况下可能这些对象是可以复用的。
所以希望某个类创建对象的数量只有一个(单例),或者是恒定数量的(多例)。
继承
继承的优缺点分析
优点
提升代码的复用性
提升代码的维护性
很好的说明了类和类之间的关系
缺点
java只支持单继承,所以导致如下问题
复用性并没有我们想象中的那么高
如果A继承了B,后期的项目开发中,随着业务扩展,导致A继承C更加合适,势必会造成代码需要大幅度修改,解决方案如下:A->B->C .....继承链过长会导致后期的维护成本增加
继承关系是类和类的强关联关系,即类之间的耦合性会增加,
但编写代码提倡的是低耦合,关联度越高,后期的维护成本越高
继承的使用
super关键字
super()
专门调用父类的构造器,保证子类创建对象的时候,
父类已经存在。默认会在构造器的首行
若this()与super()在一个构造器中顺序排列会报错,因为这两个都要争抢第一行,就会报错,
但是每个构造器中都会默认调用super(),故删掉super(),再写this()就没事,
最后调用顺序依然是super()第一个加载后续是this()
默认情况下存在一个super()调用父类的空构造器
如果子类没有构造器的互相调用,那么子类的每个构造器首行都存在一个super()执行父类构造器
super.
可省略
除了不可省略,剩余全部可以省略
不可省略
子类调用父类的属性或者方法时不可省略
this与super关键字的区分
this
子类对象,
区分局部和成员变量
在构造器首行调用this()调用构造器
创建顺序是先静态后代码块后成员(先父类后子类)
不能同时存在,必须都在方法第一行
可以区分同名问题,无同名时可以直接使用
都是对象,无法在static里面使用
super
父类对象,
在构造器首行通过super()调用其他构造器
在首行不指定会默认写super()
Object
老祖宗类,java中所有类的父类,java所有类都会直接或间接继承自object类
一个类如果没有显式的继承自其他类,则默认继承自object类
clone(),getclass(),notify(),notifyAll(),wait(long timeoutMillions,int nanos),hashcode();
toString()
System.out.println的含义
输出语句在输出一个对象的时候,会默认调用当前对象的toString方法
Object的toString方法打印输出当前类的
全限定名(当前类的包名,类名)+@+当前对象的由hash值转换得到的16进制数
一个对象的hash值就是一个唯一标识,所以可以将其看作是当前对象的地址
equals()
比较两个引用类型的对象是否相等
==可以比较引用类型的,但是比较的是两个引用类型的地址。如果两个对象是new出来得到的结果一定是false因为所有的类都是Object的子类,所以所有的类都拥有一个equals方法,如果该类中没有重写equals方法,那么就会调用Object类中的equals方法,而且Object的equals中是通过==比较的
但是如果两个用户的属性都相同,便认为是同一个对象。Object中的equals方法不满足子类的要求,便需要重写
方法的重写
重写[覆盖]是有条件的:
一定要存在继承关系
子类中编写了和父类中同名的方法
剩余的修饰符、返回值类型、参数列表一定要一模一样
基本数据类型要完全一致,如果是引用数据类型,则要小于原方法的类型
被private,final,static修饰的方法不能被重写
子类重写的方法,权限要>=父类该方法的权限(比如父类权限为default,子类的方法最低也得是default)
现阶段判定重写的方式:就是在子类中认为是重写方法的上面编写@Override 如果代码不报错,就是重写,反之即不是重写
特点
~1、方法名必须相同
~2、参数列表必须相同
~3、修饰符:范围可以扩大:public》protected》default》private
~4、抛出的异常:范围可以被缩小但不能被放大。classnotfoundexpevtion--->expection(大)
方法的重载
同一类 多个同名方法 参数列表不同(个数顺序类型不同)
多态
多态:一种事物的多种状态(形态,行为)
最终体现:父类引用指向子类对象
需配合将子类对父类方法的重写,才能算多态,否则多态无意义
调用:存在重写则调子类,否则调父类
动态的绑定,多态,由于重写的存在
多态做题四大原则
继承链:自己没有找父类
编译看类型、确定方法,运行找对象
就近最优原则
父类引用对子类新增方法不可见
先确定方法再做题,会迎刃而解
多态产生的4个条件:
有继承关系
有方法重写
父类变量指向子类对象
调用重写方法
小口诀:成员变量:编译看左边,运行看左边 成员方法:编译看左边,运行看右边因为成员方法可以重写,但是成员变量无法重写,所以成员变量名依然是左边
转型
子类-->父类 小变大 自动转(An an=new Dog();)
父类-->子类 大变小 需强转c=(Cat) an;
可以使用子类独有的内容,不能使用子类其他内容
判断一个类是否是一个类的子类,可以使用instanceof()方法
多态的作用:1增加了程序的扩展性(即多态)2增加了程序的可维护性(即继承)3把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异, 写出通用的代码,做出通用的编程,以适应需求的不断变化
abstract关键词
抽象类
实例:开发部门的java工程师和web工程师都有work这个行为和一些其他共同的行为和属性,但是不清楚这个行为具体怎么做,于是可以抽象出来一个类,这个类有一些子类共同的方法和属性
abstract关键字可以修饰类和方法。修饰的类称之为抽象类、修饰的方法称之为抽象方法
抽象类和普通的java类几乎一样,唯一的区别在于抽象类可以在内部定义抽象方法(抽象类中不需要必须有抽象方法,
即抽象类不一定有抽象方法,但是有抽象方法的类一定是抽象类。抽象类中可以任何属性方法都不定义,但是有抽象方法的一定是抽象类,单指类,接口不涉及)
抽象类是不能实例化的,也就是说抽象类是不能创建对象的。但是可以定义构造器,因为这个构造器是给子类用的
抽象类是被public修饰的,被private,static,final,native)修饰的话,抽象类也将失去意义
native
native(本地方法,看不到方法体例如public native hashcode()
抽象类可以实现接口,但不能继承接口,可以部分实现接口,这样为开发者提供相当的便利性,这个抽象类称为便利类
抽象类的意义
1 完成了多态的先决条件
2 避免了子类的随意设计
抽象方法
通过abstract修饰的方法称之为抽象方法,抽象方法指定以方法的声明,不需要编写方法体
语法: 修饰符 abstract {void|数据类型} 方法名([参数列表]);
抽象方法只能被定义在抽象类中,不能在普通类中定义,将类与接口分离开的话,拥有抽象方法的类一定是抽象类
抽象方法无法被static修饰,因为如果被static修饰了的话,则可以通过类名来直接调用该方法,但是抽象方法是没有方法体的,所以无法被static修饰
抽象类中的抽象方法只能被public或者protected修饰
一个类继承了抽象类后必须要做决定
要么将父类中所有的抽象方法全部实现
要么自己也是个抽象类
接口
接口是特殊的抽象类,是一种更规范,比抽象类更抽象的类
接口中定义的内容
接口声明的时候,需要定义为interface,接口前面可以写修饰符,只能写public修饰符。(private修饰符直接将接口的意义推翻)
接口中定义的成员变量默认都是被public static final修饰的,来达到规范的效果,即需要保证接口定义的变量必须要初始化
接口中不能定义构造器,即接口不能实例化,且不能拥有子类
接口中可以定义普通的static方法,且该方法可以拥有方法体,静态方法会被public默认修饰,
也可以改为private修饰,不可以被protected或default修饰,即要么可以被所有其它类调用,要么其他类都不可以调用
接口中被private修饰的static方法虽然不能被外部调用,但是可以作为接口内部的工具方法使用
接口中定义的普通方法默认为抽象方法,抽象方法没有方法体,接口中的抽象方法无法被除public以外的修饰符修饰,
若被default修饰,则会成为默认方法,就不是抽象方法了
JDK1.8之后,接口中可以定义default方法,
default方法可以写任意个没有数量要求,default方法需要写方法体,因此不是抽象方法,但实现类中也可以重写该方法
default必须通过实现类对象进行调用
接口中的方法要么是被static修饰的静态方法,只能通过接口直接调用,要么是抽象方法或者默认方法(被default修饰的方法),
抽象方法需要被实现类实现,默认方法可以被实现类重写,静态方法无法被实现类重写,只能通过接口名来调用
类和接口的关系
一个类可以实现很多的接口,接口和接口之间通过","分割,对于当前的类而言,我们将其称之为实现类。
实现类实现多个接口之后,需要重写当前所有的接口中的抽象方法,或者实现类自己是抽象类
接口是允许多继承的,当一个类实现了某个接口,且这个接口继承了N个其他接口,此时实现类需要重写该接口以及该接口的父接口的所有抽象方法。
同时继承与实现,则先extends再implements
接口的使用
目的是为了避免随意设计类,设计方法(方法的参数与返回值)
设计时如果父类与接口都可使用则最好使用接口
接口也是类,能够使用封装继承多态,ABC-》D,有封装与继承,
使用的时候,用接口指向实现类对象,即接口变量指向实现类对象
此时接口引用指向实现类,通过方法限定要接收的对象,这个对象只能使用对接口重写的方法
接口多态的作用
方法形参为接口类型,实参为接口实现类对象
方法形参为引用类型,实参为对应类型的对象或子类对象
不知道方法形参是否为null,可以通过if(形参!=null)来进行判断,可以增强程序的健壮性
作用与优点
面向接口最大的优势:扩展,更加灵活,实现解耦,降低耦合度,统一接口规范
接口可以多实现
接口可以规范子类(实现类)的行为
static关键词
能修饰的内容:
类 可以修饰内部类
方法 可以修饰,静态方法(也叫类方法),不是static修饰的叫做非静态方法 也叫做对象方法、实例方法
变量 只可以修饰成员变量
使用
static调用static内容 直接调用(若涉及继承等等,尽量做到通过类来调用本类中的static内容)
static调用非static内容,要创建对象 通过对象调用(静态方法调用本类的非静态成员变量需要通过new新的对象来调用,
不可以通过this关键字来调用非static内容,也不可以通过super关键字调用父类的非static内容)
非static调用非static内容 直接调用
非static调用static内容 直接调用
静态内容隶属于类,类存在静态内容就存在,非静态内容隶属于对象,对象存在,非静态内容才能存在
静态方法属于类,所以父类或者接口中的静态方法无法被重写,即子类或者实现类无法获得父类或者接口中的静态方法
类的静态方法表示类的行为,类的实例方法表示类对象的行为
类变量与static变量的区别
二者都属于类成员变量,static修饰的为类变量(也称为静态变量),无修饰的称为实例变量。
内存空间
final关键词
最后的,最终的(成员,局部变量都能修饰),被final修饰的变量为常量,修饰的方法不能被重写,类不能被继承(变为太监类)
java值传递:基本数据类型是数据值传递,
引用数据类型是地址值
final修饰的类是用来防止继承
final不能修饰抽象类,抽象类就是为了继承然后让子类重写方法,
如果被final修饰了的话就无法被继承,即抽象类将毫无意义
final修饰的方法就是用来防止重写的
内存分析
new关键字
过程
1 开空间 在堆内存中开辟一份空间出来
2 初始化 给类中的成员变量进行初始化赋默认值
3 指向引用 将堆内存中的地址赋给局部变量进行存储
Mvc
Javabean类:泛指一系列类的统称--->实体类
类是公共的
属性私有化
私有属性的公共的访问方式
至少一个空构造器
重写toString方法
重写equals方法
内部类(了解,理解)
概念
类中定义类,具有类的特点,可以继承,实现接口,类中定义成员等
成员特点可以被修饰符修饰,于是private是私有内部类,static是静态内部类,局部位置是局部内部类,匿名后是匿名内部类,1.8后可通过lambda表达式使用
成员内部类
注意点:除了静态常量能被定义,静态内容需定义在静态内部类中
内部类可以使用外部类私有和静态内容,
外部类可以通过new对象来使用内部类的成员(私有也行)
内部类使用外部类:直接使用,因为内部类中都持有一个外部类对象的引用:外部类.this
外部类使用内部类:直接使用,创建内部类对象,通过对象调用
其他类使用内部类:
或者先创建外部类对象在创建内部类对象
私有内部类
多了:
私有内部类其他类无法访问,只能所在的外部类通过创建对象使用
静态内部类
多了一层静态的含义
假设推理法,演绎推理法
编译器看,语法是否合乎习惯,是否合乎规范,是否合乎语法等
多了一个static,就是可以用静态语法,类名.静态内容。
用成员语法,通过new对象来访问
内存创建顺序不同
外部类可以随意使用内部类,这个是常识
局部内部类
定义在方法中的类称之为局部内部类。随着方法的执行而开启,方法的结束而销毁。开启与销毁指的是类的生命周期
可定义非静态成员内容,除了静态常量。
使用所在方法的局部变量,这个变量默认被final修饰,是因为gc堆栈和作用域机制,为了保证数据一致性
jdk1.8之后,默认被final修饰,1.7及之前需手动
局部内部类可使用外部类成员(关注是否为静态内容)
依旧遵守类成员,静态内容,语法,多了一个局部,其他地方都用不了,只能在方法体内使用
只想在方法内使用生存周期只在方法内
以后也不会被用到。降低了内存消耗,让业务更加具备面向对象的能力。在业务中抽取了一些共性,让代码编写更加优雅。
匿名内部类
是什么:用来简化符合要求的子类或实现类对象,他没有自己的本身作用,实现接口重写方法,只在当前使用一下
为什么:就是只能使用一次的类称之为匿名内部类,快捷键生成。
一般我们用这个匿名内部类来接口的实现类对象和抽象类的子类对象
全称:匿名内部类对象-》实现类对象,子类对象
怎么做:new一个接口类实现类子类对象+类体{};重写方法
使用方式:1.在当前使用一次:new inteface(){重写内容}.test();
2.使用多次:将对象赋值给,引用,记录地址,来多次使用
3.在方法实参使用,将对象实参赋值给方法的形参,使用方法时在()里面new对象
lambda表达式
是什么:简化符合要求的匿名内部类
前提:满足函数式接口(jdk1.8之后特性),即只有一个可被重写的抽象方法,
通过@FunctionInterface检查一个接口是否为函数式接口
lambda表达式结构()->{}
():重写的函数的参数列表
->:lambda表达式的符号,具有上下文推导作用
{}:要重写的方法的方法体
标准写法:Swim s=(参数列表)->{方法体};
写法2:方法体一句,省略{}
写法3:参数列表的参数类型可省略
写法4:1个参数时可省略()
写法5:如果方法体有且只有一个返回语句,则{}与return都可省略
简单抽象,后期与流式编程一起使用
Lambda表达式
作用
简化匿名内部类对象
前提要求
函数式接口
语法
() ->{}
() 要重写的抽象方法的参数列表
{} 重写的方法体
->lambda符号,箭头函数
lambda表达式写法
写法一
() ->{方法体语句}
写法二
当lambda体中语句只有一句的时候,前后的{}可以省略
()->System.out.println("快乐学习");
写法三
在lambda体中语句只有一句的时候,并且如果存在参数,lambda结构中参数的数据类型可以省略
(x,y) ->System.out.println(x+y);
写法四
在写法三的条件下,参数只有一个,参数前后的()可以省略
x->System.out.println(x);
写法五
如果lambda体只有一句,并且这一句为return语句,则前后的{}与return关键字可以一起省略
x->x>0;
函数式接口
定义: 内部只有一个必须被重写的抽象方法
java.util.function包下提供了一系列的函数式接口
检查: 通过注解@FunctionalInterface
四大内置函数式接口
消费型接口(需要参数,无返回值)
Consumer<T>{ void accept(T t);}
函数型接口(需要参数,有返回值)
Function<T,R>{ R apply(T t);}
供给型接口(无需参数,有返回值)
Supplier<T>{ T get();}
段言型接口(需要参数,返回值为布尔型)
Predicate<T>{ boolean test(T t);}
方法引用
作用: 简化lambda表达式,是lambda表达式的另一种表现形式
前提:lambda表达式的实现,已经有其他方法提供了实现,可以直接调用那种已经实现的方法,即可以直接通过方法引用去引用那个方法
分析步骤要求
1 lambda体是否是通过调用另一个方法实现的
2 内部所引用的方法的参数列表与返回值要求与lambda表达式的参数列表与返回值是否可以保持一致,一一对应
3 内部所引用方法的返回值要求对应lambda的返回值,lambda参数列表的第一个参数作为内部调用方法的对象存在,lambda参数列表的第二个参数往后,能够一一对应内部所引用方法的参数列表
语法
方法引用-->::
对象::实例方法
类名::静态方法
类名::实例方法
注意
lambda可以将行为作为参数进行传递(行为:功能的实现,操作方式,步骤)
内部类作用
作用和特点
1.内部类提供了某种进入其外围类的窗口。
2.每个内部类都能独立的继承自一个(接口的)实现,所以无论外围类是否已经
继承了某个(接口)的实现,对应内部类都没有影响。
3.内部类使得多重继承的解决方案更加完整。虽然接口解决可部分问题,
但是内部类有效的实现了多重继承。也就是说:内部类允许继承多个非接口
类型(类或者抽象类)。 我认为这是内部类最重要的一个作用。
4.内部类可以有多个实例,每个实例有自己的状态信息,与外围类相互独立。
5.单个外围类中,可以让多个内部类以不同的方式实现统一接口,或者继承 同一个类。
6.创建内部类的对象并不依赖外围类对象的创建。
7.内部类没有令人迷惑的is-a的关系,是独立的实体。
异常
什么是异常
是什么:程序无法正常执行,遇到异常,需要去处理
异常的体系分类
Throwable
Error
一般为虚拟机生成并脱出的,不由程序控制
Exception
由虚拟机监控到的一些可控的异常,程序员需要通过异常的提示信息进行修改程序,解决问题。
Exception分为两大类异常,一个是RuntimeException,另一个是CheckedException
RuntimeException
运行时异常,即程序运行期间发生的异常,
可以通过增强程序的健壮性处理如:1.添加if语句进行多种条件的判断,来达到尽可能的在各种条件下都能运行。
2.或者异常解决方案来解决,即报错
常见的运行时异常:
1.空指针异常: java.lang.NullPointException
2.数组索引越界异常: java.lang.ArrayIndexOutOfBoundsException
3.数组长度负数异常:java.lang.NegativeArraySizeException :arr[-1]
4.类型转换异常:ClassCastException(向下转型)
5.算数异常:java.lang.ArithmeticException:1/0
6.数字格式转换异常: java.lang.NumberFormatException
例如:Integer.valueOf("123abc")
分支主题
CheckedException
检查时异常 | 编译时异常 ->程序编译期间发生的异常,检查语法
分支主题
这种异常只能通过异常方案处理(如果检查时异常不处理,程序无法运行)
异常处理方案
异常抛出:
方法签名上throws 异常,方法体throw,抛出到上一层,使用者处理(从方法内部抛出到方法上,由调用方法者处理)
重写方法对异常抛出的要求: 重写方法上抛出的异常类型 < = 被重写方法上异常的抛出类型
异常捕获:
一个try后面可以跟着1~N个catch,多个catch中,接收明确的异常对象的catch放在前面,接收大范围类型异常对象的catch需要放在后面
try...catch...finally流程:
执行try中的代码段,如果整个代码段中没有出现异常,则不执行catch,
直到结束try..catch结构,然后最后执行finally语句
如果try中代码段运行期间出现了异常,出现异常的代码段到try结束将不再执行,
产生一个异常对象,然后拿着异常对象与catch中的引用进行比较,
直到满足一个catch接收类型就执行对应catch大括号中的代码
捕获到后,会继续执行finally后边内容
finally无论如何都会执行,除非虚拟机中断,即使catch中有return,finally也会去执行
如果所有的catch中的异常种类都不满足,JVM会中断程序的执行
自定义异常
自定义的异常类型为运行时异常,要求直接或者间接的继承RuntimeException,编译时异常是继承自Exception
throw 制造异常,throws在方法签名上,可构造器重载,调用父类方法
语法:throw new 异常名();
常用类****
object类
object类:老祖宗类,是java中所有的类的父类,java中所有的类都是间接或直接继承自object类的,如果一个类没有显式的继承其他类,则会默认继承object类
wait(),wait(long timeOutMillions),wait(long timeOutMillions,int nanos)
notify(),notifyAll()
hashcode()
toString()
返回对象的字符串表现形式:包名.类名@+十六进制的整数=getclass().getName()+"@"+Integer.toHexString(hashcode())
equals()
(健壮性)原比较地址值(对象类型),通过重写后先比较地址值再比较内容
jdk包内很多都重写了equals方法,例如string类
clone()
clone() 克隆,拷贝
来自Object类clone()方法
Cloneable接口 : 对象的类型要求实现Cloneable,才具有克隆的能力
java.lang.CloneNotSupportedException : 抛出此异常表示已调用类Object中的clone方法来克隆对象,但该对象的类未实现Cloneable接口。
方法重写的细节: 子类中的重写方法 父类中被重写的方法
== 方法签名完全相等
<= 返回值类型 : 基本数据类型要求完全相等 引用数据类型:重写方法的返回值类型<=被重写方法的返回值类型
>= 权限修饰符 : 重写方法的权限修饰符>=被重写方法的权限修饰符
浅拷贝|浅克隆 : 当拷贝对象的时候,对象的属性如果为引用数据类型,属性所指向的对象不拷贝
深拷贝|深克隆 : 当拷贝对象的时候,对象的属性如果为引用数据类型,属性所指向的对象也拷贝一份
建议: 当对象的属性是通过复杂的运算或者算法计算出来,在想要等到相同内容的对象时候可以使用clone克隆,提高效率
getClass()
Arrays类
copy()
都来自于System.arraycopy
(数组1,起始位置,数组2,起始位置,复制的长度)方法
在java.util.arrays中
static int[] copyOfRange(int[] original, int from, int to)
将指定数组的指定范围复制到新数组中。
static int[] copy(int[] original,int newLength):
使用0复制指定的数组,截断或填充(如有必要),以使副本具有指定的长度。
fill()
static void fill(int[] a, int val),将指定的int值分配给指定的int数组的每个元素
static void fill(int[] a, int fromIndex, int toIndex, int val) 将指定的int值分配给指定的int数组的指定范围的每个元素。
static void sort(Object[] a)
默认对数组中的数据做升序排序,
如果A对应的类继承了Comparable并且重写了compareTo方法,那么sort排序将按照compareTo方法体中的逻辑进行排序
字符根据unicode
字符串的比较通过首字母比较和后续的字母通过Unicode比较
static int binarySearch(int[] a, int key) 使用二进制搜索算法在指定的int数组中搜索指定的值
该方法要求参数数组必须升序排序。
如果数据不存在,则返回 -插入点的索引-1
如果查询的数据比数组中的所有数据都小,
那么返回值将会是-1即(-插入位置-1)=(-0-1)=-1
equals(byte[] a, int aFromIndex, int aToIndex, byte[] b, int bFromIndex, int bToIndex)方法,
或者
equals(byte[] a, byte[] a2)
先比长度,后比内容,左闭右开
toString()获取数组的内容,不是重写是重载方法
asList(T... a)方法:将数组转成列表
二维数组的内容或者俩数组比较
DeepToString()
DeepEquals()
DeepHashcode()
Math类
static double abs(double a) :返回double类型的绝对值
static double ceil(double a) :返回大于或等于参数的最小整数(向上取整)
static double floor(double a) :返回小于或等于参数的最大整数(向下取整)
static double max(double a,double b) :返回两个doube值中较大的double值
static double min(double a,double b) :返回两个double值中较小的double值
static double pow(double a,double b) :返回a的b次幂
static double random() :返回带有正号的double值,大于或等于0.0且小于1.0
公式:[min,max)->(int)(Math.random()*(max-min))+min
[min,max]->(int)(Math.random()*(max-min+1))+min
static long round(double a):返回与参数最接近的long,判断四舍五入是根据小数点后一位
static double sqrt(double a) :返回double值得正确舍入平方根
Math.PI 数学π
枚举类型
通过enum关键字定义枚举类型
描述一种事物的所有可能或一个类型的所有实例
使用可根据类名.静态实例来使用
自定义的枚举类型默认是隐式继承自java.lang.Enum,他是公共基类
name()返回字段名,ordinal()返回字段序数,从零开始,values()获取所有实例字段,
switch在jdk1.5后可以使用枚举类型字段来判断
通过枚举类的字段(也就是实例)提供当前枚举类型的实例,默认的被public static final修饰
枚举类型中的构造器默认私有化
枚举类中可以定义成员(即实例),成员变量,构造器,方法,根据需要定义属性功能构造器等
一般是直接定义字段,直接拿枚举类型字段实例的地址去直接比较,不用去赋值,这样更简单
String类*****
String类表示字符串。 Java程序中的所有字符串文字(例如"abc" )都实现为此类的实例在源码中jdk11 下,是 private final byte[] value; jdk8下private final char[] value;
可能考的题
字符串翻转
不可变字符序列,""默认都是String类型,底层是字节或字符数组
jdk8是char[]数组,jdk11是btye数组,为了提升效率
执行效率:StringBuilder>StringBuffer>String
安全性:String>StringBuffer(线程同步)>StringBuilder(线程不同步)
面试题
String s1="abc" 1个对象在常量池中
String s2=new String ("haha") 2个对象 1个在堆 1个在常量池
String s3=new String ("abc") 1个对象 1个在堆 另一个已经在常量池中
构造器**
在String里,new会创建一个对象,"abc"这样子的字符串文字本身也是对象
String():创建一个空字符序列
String(char[] value):分配新的 String ,使其表示当前包含在字符数组参数中的字符序列
或new String(char数组,0(索引),3(长度))
String(byte[] bytes) :通过使用平台的默认字符集解码指定的字节数组构造新的 String(utf-8编码中,1个汉字对应3个字节;GBK编码中,1个汉字对应2个字节)
String("对应的字符串") :初始化新创建的String对象,使其表示与参数相同的字符序列; 换句话说,新创建的字符串是参数字符串的副本
常用方法
**char charAt(int index) : 返回指定索引处的 char值
compareTo()与comparetoIgnoreCase()不区分大小写比较,返回int,1大于,0等于,-1小于
String concat(String str) :将指定的字符串连接到此字符串的末尾并返回新的字符串
*copyValueOf()将字符数组转为字符串
*getBytes()返回字节数组,可带类型gbk utf-8等等
*****equals()判断两个字符串内容是否一致
***indexOf()返回字符或字符串第一次出现的索引位置,可设置起始offset位置
***lastIndexOf返回最后一次出现的位置
intern(),sb=“a”+“b” 在做字符串相加时,底层是StringBuilder做的,在堆中创建”ab“对象,,没有在常量池创建对象,用intern()后会把当前对象地址传入常量池,故再去String a=“abc“,此时 a=sb, 因为地址一样(jdk1.7及之后特性)
*****length()返回当前字符串的长度
***replace(老字符串,新字符串)用新去替代老的,可用正则表达式
replaceAll()同理
*repeat(n)将一个字符串重复n次连接起来并返回
**boolean contains(CharSequence s): 当且仅当此字符串包含指定的char值序列时,才返回true
***String[] split(String regex): 将此字符串拆分为给定regular expression的匹配项返回字符串数组
*****String substring(int beginIndex): 返回一个字符串,该字符串是此字符串的子字符串
*****String substring(int beginIndex, int endIndex): 返回一个字符串,该字符串是此字符串的子字符串
**char[] toCharArray(): 将此字符串转换为新的字符数组
***String strip()与String trim() 这两个方法均可以将原字符串中前后的空格符删除,
但是trim方法只可以删除半角空格无法删除全角空格。strip方法既可以删除半角空格也可以删除全角空格
**toLowerCase(),toUppercase将字符串字母变大小写
***String.valueOf()返回各种类型,基本数据类型以字符串表示的形式
StringBuffer类与StringBuilder类*****
StringBuffer和StringBuilder都是为可变字符序列
区别:
共同点:
都是字符序列,实现相同的CharSequence接口,都表示字符串
底层都是通过字节数组存储字符串中的数据(老版本jdk使用字符数组)
不同点:
是否可变:
String 为不可变的字符序列
StringBuilder 可变字符序列
StringBuffer 可变字符序列
执行效率:
StringBuilder>StringBuffer>String
线程安全 | 同步:
StringBuilder 线程不安全 | 不同步
StringBuffer 线程安全 | 同步
通过synchronized同步锁控制安全
StringBuilder:
一个可变的字符序列。 此类提供与StringBuffer兼容的API,但不保证同步。 此类设计用作StringBuffer替代品,用于单个线程使用字符串缓冲区的位置(通常情况下)在可能的情况下,建议使用此类优先于StringBuffer因为在大多数实现中它会更快
初始容量:
以后某些类型结构中,如果底层存储数据的结构式数组,一般都会存在初始容量,初始容量指的是数组最初始的长度容量
StringBuffer与StringBuilder的底层结构数组的初始容量是16
new对象时,初始容量是new的字符长度+16,底层实现是16+str.length()
可new对象时指定容量值:new StringBuilder(int capacity)
扩容机制:
int newCapacity=(oldCapacity<<1)+2; 即扩容为原容量的两倍+2
使用Arrays.copyOf()底层实现,是数组的创建和拷贝内容
方法***
lenth()返回长度
capacity()返回容量
StringBuilder()创建空字符串
StringBuilder(int capacity)创建空字符串时指定容量
StringBuilder(“ABC”)创建字符,初始容量为16+str.length()=19
append(),添加数据
delete(int begin,int end)删除指定范围内的字符串
insert(int offset,String str)在指定索引位置插入字符串
reverse()翻转字符串并返回
包装类*****
byte的包装类为Byteshort的包装类为Shortint的包装类为Intlong的包装类为Longdouble的包装类为Doublefloat的包装类为Floatchar的包装类为Characterboolean的包装类为Boolean
jdk1.5新特性--->自动拆箱装箱装箱: 基本--->引用拆箱: 引用--->拆箱
有了基本类型为什么还要提供包装类?
1容器中只能存储对象数据,引用数据类型
2包装类中封装了一些成员,方法,方便使用,灵活,功能强大
3引用类型的属性 | 字段,可以区分比较,比如是否新用户 | 已经消费为0的两种状态,null为还没充钱,0为充钱了但是花光了
为什么有了包装类还要保留基本数据类型?
1使用方便,简单
2有利于节省内存
Integer类对象比较问题:在表示的数据值相同的情况下,用==进行比较
1 如果两个基本数据类型比较,值相等就相等
2如果两个new Integer比较,肯定不相等,因为new是开辟新的对象地址
3如果是int与Integer比较,无论是自动装箱还是new Integer,只要值相等就相等,因为会发生自动拆箱,然后再比较基本类型数据值
4如果两个Integer比较,且这两个Integer均为通过自动装箱方式,如果数据值在[-128,127]之间,
就会返回缓冲区对象,即这两个引用就相等,若不是这个区间,就会返回new Integer(),那么就会不相等
api
Integer.valueOf()装箱
String.valueOf()转为字符串
in.intValue()拆箱
byteValue(),intValue(),floatValue()等等拆箱
max().min()
paseInt(String s)或(String s,radix进制)转为Integer
toString()
toHexString(),toBinaryString(),toOctalString()
日期时间相关类
jdk8之前就提供--->Datejdk8 新增
Date类:Date类表示特定的时刻,精度为毫秒
构造器
Date(): 分配Date对象并对其进行初始化,使其表示分配时间,测量 Date到毫秒
Date(long date): 分配 Date对象并初始化它以表示自标准基准时间(称为“纪元”)
以来的指定毫秒数,即1970年1月1日00:00:00
常用方法
long getTime(): 返回自此 Date对象表示的1970年1月1日00:00:00 GMT以来的毫秒数
boolean after(Date when): 测试此日期是否在指定日期之后。
boolean before(Date when):测试此日期是否在指定日期之前
equals(),toInstans()了解
SimpleDateFormat类:日期格式转换器
日期和时间模式
它允许格式化(日期->文本),解析(文本->日期)和规范化
构造器
SimpleDateFormat():构造一个 SimpleDateFormat使用默认模式和日期格式符号默认 FORMAT区域设置
SimpleDateFormat(String pattern): 构造一个 SimpleDateFormat使用给定的模式和默认的默认日期格式符号 FORMAT区域设置
常用方法
日期转为文本: String format(Date date) 将给定的 Date为日期/时间字符串
文本转为日期:Date parse(String text) 解析字符串中的文本以生成 Date(需要注意parse方法中的日期格式需要与调用该方法的SimpleDateFormat对象中定义格式相匹配,即如果是yyyy-MM-dd格式,那么参数也应该是这种格式)
常用"yyyy-MM-dd E hh/HH:mm:ss SSS"
"yyyy-MM-dd hh:mm:ss"
IDK8当中提供的
LocalTimel 、Localate , LocalDateTime类,
都可以通过now()方法获取当前时间日期,
通过of()方法构造一个指定的时间日期,
通过一系列get()方法获取对象中的日期时间信息。
与老的api不同,它们都是类似String的不可变对象。
简介
分支主题
java8新增时间日期API :
Date,SimpleDateFormat 线程不安全的
JDK8当中提供的`LocalTime` 、` LocalDate` 、 `LocalDateTime`等类型,线程安全,使用简单灵活
使用
//Year,Month
Year.now()Year.of(2022)
Month.FEBRUARY
//LocalDate
LocalDate today = LocalDate.now();
LocalDate yourBirthDate = LocalDate.of(1999, Month.JUNE, 15);
//LocalTime
LocalTime time = LocalTime.now();
LocalTime time2 = LocalTime.of(11,11,11);
//LocalDateTime
LocalDateTime now = LocalDateTime.now();
LocalDateTime ldt2 = LocalDateTime.of(2011,12,12,10,18,24);
//获取
getYear());
getMonth());
getDayOfMonth());
getDayOfWeek());
//操作
isEqual().isLeapYear()
.minusYears(1)
plusYears(1)
now.withYear(2028)
//日期转换器
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String nowStr = now1.format(formatter); //放入格式器
String dateStr= "2020-02-25 11:23:04";//转成日期
LocalDateTime date= LocalDateTime.parse(dateStr, formatter);//放入格式器
//间隔
Period p = Period.between(birthDate, today1);
long between = ChronoUnit.YEARS.between(startDate, endDate);
IO流***
IO前知识:File类
构造器
常用/,//,///,///,/////////或\\(必须)
File(String pathname) 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例(重要)
File(String parent, String child) 从父路径名字符串和子路径名字符串创建新的 File实例
File(File parent, String child) 从父抽象路径名和子路径名字符串创建新的File实例
常用方法
boolean canWrite() 测试应用程序是否可以修改此抽象路径名表示的文件
通常逻辑是,是否存在,创建,操作即增删改查
看是否存在,然后创建是为:增加:exists()boolean mkdir() mkdirs() createNewFile() ;
删除,更改:boolean delete()setReadOnly()renameTo()
查找:listFiles(),list()canExecute(),canRead()getName(),getParent()getAbsolutePath()length()lastModifier()
其他查找:getFreeSpace(),getusableSpace(),getTotalSpace()
canExecute(),canRead()
setReadOnly()
exists(),getName(),getParent()
boolean delete() 删除该file对象指定的文件夹或者文件
boolean createNewFile() 当且仅当具有此名称的文件尚不存在时,以原子方式创建由此抽象路径名命名的新空文件
boolean mkdir() 创建此抽象路径名指定的目录(只能创建一层目录)
boolean mkdirs() 创建此抽象路径名指定的目录,包括任何必需但不存在的父目录
getAbsolutePath()获取绝对路径,默认放在项目根目录下
getFreeSpace(),getusableSpace(),getTotalSpace()
isAbsolute(),isDirectory,isFile(),isHidden()
lastModifier()返回最后一次修改时间
length()返回文件大小长度
listFiles(),list()返回文件路径文件对象,或路径,可加过滤器,可用于循环查找删除
renameTo()重命名
IO流
流的分类
根据流向分
输入流
输出流
根据操作单原划分
字节流:万能流
字符流:只能操作纯文本内容
按照功能分
节点流:真实操作数据,从数据源到目的地之间传输
功能流(包装流):从数据源经过包装再到目的地---->增强节点流的功能,提高节点流的性能
字节流*****
InputStream字节输入流
此抽象类是表示输入字节流的所有类的超类。需要创建的是具体子类的对象,实现数据的传输
根据数据源的不同选择不同的子类: ---->节点流
数据源为文件:FileInputStream 文件字节输入流
数据源为字节数组:ByteArrayInputStream 字节数组输入流
OutputStream字节输出流
此抽象类是表示输出字节流的所有类的超类。需要创建的是具体子类的对象,实现数据的传输
根据数据源的不同选择不同的子类------>节点流
数据源为文件:FileOutputStream 文件字节输出流
数据源为字节数组:ByteArrayOutputStream 字节数组输出流
常用方法
read()返回读取的字符的int值,如果读取到最后是返回-1,close()关闭流
read(byte[] b)(off,len)读取内容给byte数组,返回读取到的长度,超过则返回-1
一般byte[] car =new byte[1024] 1024的倍数,
然后使用new String(byte b[])转成字符串
readAllBytes(),读取剩下所有内容,返回一个字节数组,jdk9特性
fileInputStream.available()获取文件的大小,字节数
FileInputStream is=new FileInputStream(File)(String),报错fileNotFoundException
步骤:1.与文件建立联系
2.创建字节输入流
3.输入
4.处理
5.关闭
fileOutpuStream(),byte
(只能操作纯文本文件)字符流
Reader用于读取字符流的抽象类
文件字符输入流FileReader:节点流 输入流 字符流无新增功能,可发生多态
Writer用于写入字符流的抽象类
文件字符输出流FileWriter:节点流 输出流 字符流
无新增功能,可发生多态
缓冲流
作用:加快读写效率
大概是都多加了一层包裹,其中文件字节输入输出流可以多态,文件字符输入输出流不能多态
字节缓冲流
字节输入缓冲流:BufferedInputStream 无新增方法,可以发生多态
字节输出缓冲流:BufferedOutputStream 无新增方法,可以发生多态
字符缓冲流
字符输入缓冲流:BufferedReader 新增方法:String readLine()读一行文字
字符输出缓冲流:BufferedWriter 新增方法:void newLine()写一行分隔符
Data流
Data流 | 基本数据类型流 | 传输数据+保留数据类型(基本数据类型+String)功能流---->字节流的功能流
Data输入流:DataInputStream 新增方法:readXxx()
Data输出流:DataOutputStream 新增方法:writeXxx()
注意
读入的数据类型与写入的数据类型的顺序应当保持一致
读入的是写出的源文件(如果随便读入一个文件,那么就不知道数据类型的排列顺序了)
可以与缓冲流一起使用,用data包裹缓冲流,缓冲流包裹字节流
Object流***
Object流 | 对象流 | 引用数据类型流:数据+类型(基本|引用)属于字节流的功能流
当读写,传输对象数据的时候,可以选择Object流,能够读写数据的同时保留数据类型
序列化输出流:ObjectOutputStream 新增方法: writeXxx(Xxx args) void writeObject(Object obj)将指定的对象写入ObjectOutputStream中
反序列化输入流:ObjectInputStream 新增方法:Xxx readXxx() Object readObject() 从ObjectInputStream中读取一个对象
序列化*****
序列化:将对象转化为可存储或者可传输的状态的过程
反序列化:将可存储或者可传输的状态转为对象的过程
注意:
引用数据类型数据为对象数据
先序列化后反序列化
不是所有的类型的对象都能序列化,必须实现Serializable接口
反序列化与序列化顺序应当一致
transient修饰的数据不会序列化
static属性不会序列化
如果父类实现序列化接口,子类没有实现序列化接口,可以序列化所有内容
如果子类实现序列化接口,父类没有实现序列化接口,则只能序列化子类的属性
序列号
实现了序列化接口的类型会默认存在序列号,
即serialVersionUID,当类中的成员改变,序列号会默认更新
作用:检查前后版本是否统一
解决版本不一致的问题:手动设置序列号
setting设置:Editor->Inspections->serializable->class without serialVersionID
使用步骤:1类实现序列化接口
2.setting设置
3.Alt+Enter 快捷键
commons-io
别人写好的工具类
常用
IoUtils:读写,拷贝,字节流,字符流
FileUtils:基于File,读写拷贝,比较文件
FilenameUtils:文件名不是对象,为了Unix与在Windows环境保持一致
FileSystemUtils(不常用)
使用步骤
可以直接通过maven导入
或者1.指定位置下载
2.在项目在创建lib,放置当前所要依赖的jar
3.将jar加入 =add ad library
4.直接使用即可
0 条评论
下一页