Java初级学习
2022-08-15 17:45:45 21 举报
AI智能生成
登录查看完整内容
java初级学习思维导图,内容非常全面,能够总览几乎所有的java初级基础内容,总主题数上千,内容完全是自己手写,一点一滴积累起来的,相信会对其他人有所帮助。 适用于,java初级学习,java入门,java扎实的基础,等。 通过该思维导图也能,检测知识的掌握程度,能够通过该导图背与掌握绝大多数内容,在面试时得心应手
作者其他创作
大纲/内容
数据类型作为参数传递
只能配置引用数据类型
参数化类型
泛型的行为发生在编译期间,运行期间泛型配置的所有内容无效,泛型擦除
要求先定义泛型,才能使用泛型
<>定义泛型
类型的后面定义泛型,在使用类型时可通过泛型传递具体类型类中可以进行使用泛型方法
泛型类
后边可省略<String>成<>或不写但标准写法是ArrayList<String> list = new ArrayList<>()
使用
代码简单简洁
增强程序健壮性,避免类型转换异常的出现
增强稳定性与可读性
泛型的优点
泛型jdk1.5
集合的特点:灵活,根据需求增删改查\t只能存储引用数据类型的数据,\t所有引用数据类型的祖宗类是object\t能够存储不同的数据
数组的特点:是引用类型,可以存对象\t定长不可改变长度的序列\t类型相同\t有序,有索引
数组与集合的区别
添加 add()addAll()
retainAll()求交集
toArray()
是否空 isEmpty()
contains(obj/col)
literator()
记数 size()
方法
通过iterator()方法获得迭代器
hasNext()方法判断是否有下一个对象
next()方法来获得对象
迭代器遍历该接口实例化的时候,不可以对容器中的元素进行修改,否则报错
迭代器遍历
Collection的遍历
该接口的实现类可以精确控制列表中每个元素的插入位置
可通过整数索引(即元素在列表中的位置)来访问元素,来搜索列表中的元素
有序集合(也成为序列)——即有序的,可重复
list
remove(index)object()
listIterator()
list新增方法
for循环
foreach
iterator迭代器
foreach和iterator迭代器在迭代期间增删,会产生异常,因为是两条线程在操作集合,导致数据不一致
列表的迭代器,允许在任一方向上遍历列表,在迭代期间修改列表,并获取迭代器在列表中的位置
列表迭代器有add()方法,可以在迭代时向List中添加对象找到立即在当前位置添加
列表迭代器有hasPrevious()方法与previous()方法,可以实现倒序遍历
列表迭代器有nextIndex()方法与previousIndex()方法,可以实现在迭代时获取迭代器在列表中的位置
列表迭代器有set()方法,可以实现在迭代时对对象的修改
listIterator列表迭代器
List的遍历
存储的每个元素为单个值,有序,可重复,可以根据索引进行操作线程不安全
动态数组 Object[] elementData
底层结构
根据索引查询效率高
做增删涉及到新数组的创建,以及原数组的数据拷贝,效率低
特点
foreach()流式编程
新增内容
与List接口相同,即四种遍历方式
遍历
容器初始容量:10
扩容机制:扩容原容量的1.5倍
存储引用数据类型时,需要在对象类型中重写equals,在比较时contains或indexof比较内部调用equals方法
注意
适用场景:大量做查询,少量做增删的时候适合使用
ArrayList*****
向量
都是List接口下的实现类,都有序可重复
底层结构都是数组
共性
Vector是同步的
ArrayList线程不安全,不同步
线程安全问题 | 同步问题
ArrayList与Vector初始容量默认都是10
ArrayList扩容:1.5倍
Vector扩容:2倍(可以设置每次扩容的容量)
StringBuilder和StringBuffer是2倍+2,初始为16
扩容机制
与ArrayList的区别
Vector与ArrayList的区别
Vector
共同点:都实现的List接口,具有List的特点,有序,可重复,可以有多个null值,可以根据索引来操作,都是线程不安全的集合。
区别:\tArrayList底层为动态数组实现,具有数组的特点,增删慢,查询快,具有索引,可以根据索引快速查询到数据,扩容机制:一开始为0,在使用add时赋初值10,以1.5倍原长扩充\tLinkedList底层为双向循环链表实现,具有链表的特点,增删快,查询慢,主要关注头尾节点,扩容机制:初始容量为0,以增加节点方式扩容
面试题:LinkedList与ArrayList的共同点与区别
有序,可重复
查询效率低,增删效率高
线程不安全
双向循环链表
双链表,实现所有可选列表操作,并允许所有元素(包括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接口相同,四种遍历方式
遍历方式
适用于大量做增删,少量做查询的情况
适用场景
LinkedList
List接口(索引)
继承自Collection接口,Set接口中无特殊新增方法,方法与Collection保持完全一致
无序,不可重复
无序即添加的数据与内部真实存储的顺序不一致
哈希表(jdk1.8之后为数组+链表+红黑树,jdk1.7之前为数组+链表)
创建了一个默认长度为16,默认加载因子为0.75的数组,数组名为table
根据元素的哈希值和数组的长度计算出存入的位置(索引)值
判断当前位置是否为null,如果是null,则直接存入
如果应存入的位置不是null,表示该位置有元素,则调用equals方法比较属性值
如果属性值一样则不存入,如果属性值不一样则存入数组,老元素挂载在新元素下面,形成链表结构。每次存入一个数据都和链表中的元素进行equals比较属性,属性值一样,则不存,不一样则存,重复以上操作
当存满了16个长度的元素怎么办呢?这是一个问题,这个问题就交给了加载因子,我们默认的加载因子是0.75。当数组中存满了 16*0.75 = 12 个元素的时候,数组就会自动扩容为当前数组长度的两倍
HashSet的底层逻辑:jdk1.7以及之前,数组+链表元素过多,查询效率低。
若我们这个链表上挂载了很多元素,每一次进行存储都会在链表中一个一个去比较,对判断的效率影响太大。故在jdk 1.8 中采用了 红黑树的结构
即当链表的长度超过8的时候,自动转化成了红黑树。小的元素在右边,大的元素在左边,这样进行查询判断的效率就提高了小于6则自动转为链表
黑红树:自平衡二叉搜索树(左右子树高差有可能大于 1)二叉搜索树又称二叉排序树,二叉查找树,就是中序遍历,是从小到大排列的
HashSet的底层逻辑:jdk1.8之后,数组+链表+红黑树效率得到了很大提升。
是简化版HashMap底层结构为:
无序,去重
查询与增删效率比较高
允许有null元素
hashset使用对象类型来计算hashcode值,hashmap使用键值来计算hashcode值
如果HashSet存储引用类型对象的时候,要在该类型对应的类中重写HashCode方法与equals方法,不然会报错,无法对容器中的元素进行默认排序
HashSet
底层由TreeMap实现(本质也是一个简化版的TreeMap)
底层结构为红黑树(平衡二叉树)会自动进行平衡,左右子树深度相差超出一定限度
底层
TreeSet内部需要对存储的元素进行排序,默认为升序排序
也可以通过自定的排序规则来实现排序
默认情况下不允许添加null元素
去重,对数据进行自动升序排序
应用场景
ceiling返回大于等于钙元素的最小数据,没有返回nullfloor
first()返回最小数据,last()
pollfirst。polllast()
higher(),lower()
subset(from,to)
新增方法
外部比较器(定制排序):\t或者通过创建一个比较器(可以匿名内部类,可以单独创建一个类)。lambda表达式,非常简化比较器需要实现Comparator接口,并重写compare方法来定义比较规则。
比较器
TreeSet
Set接口
节点数组+单向链表
16
初始容量
0.75(一般不会修改)
加载因子
当前容量*加载因子,即存储的数据个数>当前容量*加载因子就会扩容
扩容阈值(扩容临界值)
扩容为原容量的2倍
哈希表
Collection接口
1 Collection容器中的元素是孤立的,即Collection容器是单例容器
2 Map容器中的元素是成对存在的,可以通过键来找到对应的值。即Map容器是双例容器
Map与Collection的区别
Map中的key是无序的,去重的 ----->类似于Set集合
Map中的value是无序的,可重复的-------->类似于Collection集合
将所有的key值获取并放入一个Set集合中,然后通过foreach遍历
将所有的value值获取并放入一个Collection集合中,然后通过foreach遍历
将所有的key与value获取并放入一个Entry容器中,然后foreach遍历
将所有的key值获取并放入一个Set集合中,然后通过迭代器遍历
将所有的value值获取并放入一个Collection集合中,然后通过迭代器遍历
将所有的key与value获取并放入一个Entry容器中,然后迭代器遍历
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视图
常用方法
红黑树
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
TreeMap存储过程中,key的排序,去重都是根据比较器决定的
TreeMap线程不安全
HashMap比TreeMap效率高
TreeMap是可以对key进行排序的容器,默认升序,也可以通过比较器来完成对应的排序操作
HashMap与TreeMap的区别
TreeMap
jdk1.7以及之前,数组+链表元素过多,查询效率低。
分支主题
jdk1.8之后,数组+链表+红黑树效率得到了很大提升。
无序,key不可以重复,value可以重复
相比于Map接口,无新增方法
允许key为null,允许value为null
查找,删除,修改的效率高
应用场景,对增删数据与查询数据都有要求的键值映射数据,且去重的场景
遍历三种方式,同map
HashMap*****
都是Map接口的实现类,底层结构都是哈希表增删查询都快,无序,key不可以重复,value可以重复
共同点
Hashtable继承于Dictionary抽象类
HashMap继承于AbstractSet抽象类
继承体系不同
HashMap 线程不安全 | 不同步
Hashtable 线程安全 | 同步
线程安全方面不同
初始容量为16,加载因子0.75,每次扩容为原容量的2倍
HashMap扩容机制
初始容量为11,加载因子0.75,原容量的2倍+1
Hashtable扩容机制
扩容机制不同
HashMap 可以存储null值的key与value
Hashtable key与value都不允许为null
键值对数据null值得要求不同
int hash = (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);int index = (n - 1) & hash
HashMap
int hash = key.hashCode();int index = (hash & 0x7FFFFFFF) % tab.length;
Hashtable
计算hash值与位桶索引index的算法不同
不同点
1 使用Hashtable
如何处理HashMap线程不安全问题
HashMap与Hashtable
Map接口
操作容器的工具类 静态工厂类似于Arrays
void shuffle(List) //对List容器内的元素进行随机排列
void reverse(List) //对List容器内的元素进行逆续排列
Collections
1 定义配置文件xxx.properties
2 定义Properties对象
3 调用load(),与从流中加载数据Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties")
4 根据key 获取value--->getProperty
从配置文件中读取键值对数据
作用,方便快捷安全,稳定
Properties
容器******
系统中的程序,系统中资源分配的最小单位,每个进程都有自己的代码与数据空间,进程之间的切换开销较大一个进程之间可以包含1~n个线程,
进程
程序中的顺序流,线程是cpu执行与调度的最小单位,每一个线程都有自己的程序计数器运行栈,线程之间切换开销较小多个线程之间共享进程的代码与数据空间,
线程
进程与线程之间的区别***
优点:提高性能、提高效率,资源利用率高缺点:设计会更复杂,很可能出现数据不安全的情况
注意有的多线程是模拟出来的,真正的多线程是指由多个cpu,即多核,如服务器。如果是模拟出的多线程,即一个cpu,那么在同一时间点,cpu只能执行一个线程,因为切换快,所以有同时执行的效果
一个cpu同一时刻只能调度一个线程
三高:高并发,高可靠,高性能
概念***
不灵活,单继承
1 继承Thread,重写run()方法+start()方法开启线程
接口多实现,更灵活,实现资源共享但返回值,异常抛出类型要小于等于原重写方法,修饰符大于等于原修饰符
优点:
静态代理模式
2 实现Runnable接口,重写run()方法+new Thread(new 对象).start()方法开启线程
ExecutorService server=Executor静态工厂.newFixedThreadPool(99)Future<Integerserver.submit(对象)integer sum=result.get()server.shutdown()
call()方法可以抛出异常,可以定义返回值,而run()方法不可以,但更复杂
3 实现Callable接口,重写call方法+线程池
还可以使用静态内部类,成员内部类,局部内部类,匿名内部类和lambda表达式来简化使用
创建线程的方式*****
new Thread()
新生状态
1 start()方法
2 阻塞解除
3 cpu的调度切换
当线程执行到yield()方法时,会让出cpu的资源,同时线程会恢复到就绪状态
4 yield 礼让线程
线程进入到就绪状态线程可以被cpu调度
就绪状态
当cpu调度一个处于就绪状态的线程时,这个线程会进入到运行状态
运行状态
一个线程如果进入到阻塞状态,阻塞解除之后,不能直接恢复到运行,会恢复到就绪状态,等待cpu的调度
static void sleep(long millis) 导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,具体取决于系统计时器和调度程序的精度和准确性
当一个线程调度sleep()方法进入睡眠状态,就会让出占用cpu的资源
抱着资源睡觉:这个资源不是cpu的资源,指的是对象的锁资源
放大问题出现的可能性
模拟网络延迟
作用:
sleep线程休眠
1 sleep()方法
join()方法 插队线程
A线程执行过程中,如果B线程插队了,A线程就会进入到阻塞状态,等待插队线程执行完毕 | 等待执行的时间,A线程会恢复到就绪状态
void join() 等待这个线程死亡
void join(long millis) 此线程最多等待 millis毫秒
2 join()方法
等待时间或指定线程来唤醒
3 wait()方法
4 IO
如何让线程进入到阻塞状态
阻塞状态
一个线程一旦进入终止状态,便不可恢复
1 正常执行完毕
2 stop() 已过时,不推荐使用
3 通过标识判断
如何让线程进入终止状态
终止状态
中断标识默认是false,被中断一次后就会变成true
void interrupt() 为线程添加一个中断标识
注意:当调用sleep()方法导致线程休眠时,如果有任何线程中断了当前线程就会报错。 InterruptedException 抛出此异常时,将清除当前线程的中断状态
中断机制
线程的状态**
getPriority()setPriority()设置优先级MAX_PRIORITYMIN_PRIORITYNORM_PRIORITY默认isDaemon()是否是守护线程setDaemon(boolean)将线程标记为用户或守护线程先设置守护再start()运行
线程优先级
分为用户线程与守护线程,用户线程结束守护线程跟着结束
垃圾回收机制就是典型的守护进程
线程分类
synchronized概念:多线程同时操作同一份资源时,可能出现线程安全问题
同步锁:让多个线程排队执行:重点是代码的范围,达到数据安全
组成:1.同步代码的范围(排队执行代码)\t2.同步的条件:对象锁(一个对象只有一把锁,当锁没有释放,\t其他线程无法获取),线程等待是阻塞状态
同步方式\t\t同步方法:把锁放到方法上\t\t静态方法:锁类的class对象\t\t成员方法:锁对象,锁this\t\t同步代码范围是整个方法体\t\t同步块:synchronized(){}\t\t()内锁类.class,锁对象,锁资源注:每个类只有一个class对象,唯一的在第一次类加载到内存时,就存在,不用创建,只能获取
同步方法优缺点:简单,但效率低,一些代码可能不需要同步同步代码块优缺点:效率块,但复杂,按需锁,范围不对可能锁不住
很多死锁都是因为逻辑问题出现的
死锁(理解)
线程安全*****
线程之间互相通信
等待,当执行到对象,当前线程会进入到与该对象相关的等待池中等待(等待队列或阻塞)等待指定时间|等待被唤醒会释放cpu资源,同时释放对象锁资源
wait()
休眠,抱着锁资源睡觉,只让出cpu资源
sleep()
唤醒,将等待池中的线程唤醒进入就绪状态,不会马上执行,等待cpu调度并获取对象资源
notify()
方法实现
线程通信
多线程***
底层数据传输
网络编程
上层应用
网页编程
网络与网页编程区别
IP:定位互联网中的节点
端口:区分同一台电脑的不同软件
协议;合同标准,规范
一些知识点
写信,邮递,协议更简单,开销小,效率高非面向连接,数据不安全(丢失),大小限制,一般不超60K
数据传输平等,基于字节数组
socket套接字:传输层为应用层开的口子,做传输层与应用层之间的传输
DatagramSocket: \tsendreceive构造器等
DatagramPacket():\t构造器发送数据(指定IP,端口接收数据(不指定
面向socket编程
1.定义发送端
2.准备数据包
3.发送数据
4.关闭
发送流程
2.准备包,用于接收数据
3.接收数据
5.关闭
接收流程
UDP
打电话,面向连接,安全,效率低,大小无限制基于三次握手,四次挥手
两端不平等,存在客户端与服务端概念服务端会阻塞式监听,客户端请求并建立连接基于Io操作传输数据
1定义客户端
2准备数据
3io流把数据发送到服务端
4刷出
5关闭
客户端流程
1定义服务端
2阻塞式监听
3获取输入流,读取数据
4处理数据
服务端流程
TCP
InetAddress(),ip地址,IPV6,16字节,128位,IPV4,4字节,32位
java.net包
192.168.0.0~192.168.255.255:非注册地址,供组织内部使用的IP
127.0.0.1(localhost):本地IP
域名解析:本地解析,服务器解析
特殊IP
区分节点中的软件
2个字节的范围:0~65535
同一个协议下端口不能冲突,建议8000以内端口不使用,预留端口号80:http8080:tomcat1521:oracle3306:mysql
端口
传输层协议
1.排队登录,循环结束再能下一次循环
2.多线程,多客户登录
多用户登录
网络编程***
IO:传输数据
Stream流: 操作数据,计算数据,数据的运算,流式编程,流式运算,链式编程对数据源产生的元素序列进行运算
数组 | 集合:存储数据
区别
1 Stream流本身不会存储数据
2 Stream不会修改数据源 | 源对象,每次会返回持有结果的新的流Stream,继续运算,不断运算
3 延迟执行 | 惰性加载 : 当获取终止行为时候,才会执行一系列的中间操作
4 流都是一次性的流,不能重复使用多次,一旦使用过就已经被破坏
四大内置函数式接口
java.util.function包提供了一系列的函数式接口供选择使用
函数式接口
类名::静态方法名
对象::成员方法名
条件一:1.lambda体是否是调用另外一个方法实现的\t2.内部所引用的方法的参数列表与返回值是否与lambda表达式参数列表和返回值一致
类名::成员方法名
条件二1.lambda体是否是调用另外一个方法实现的\t2.内部所引用的方法的参数列表与返回值是否与lambda表达式参数列表和返回值一致,lambda第一个参数作为内部调用方法的对象存在,第二个参数后一一对应
格式
当lambda体({})的实现,是通过调用另外一个方法实现的时候,考虑是否满足方法引用要求,可以通过方法引用直接引用那个方法简化lambda
方法引用
lambda表达式
1)Collection ->streamlist.stream()
2)Arrays ->stream(数组)Arrays.Stream(arr)
3)Stream.of(值列表)
1 创建Stream
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,流中流,将所有个流汇成一个新流并返回,将二维数组转为一位数组
2 一系列流式的中间操作(都会返回一个持有结果的新的流)
1 遍历 foreach(Consumer)
allMatch() 检查是否匹配所有元素
anyMatch() 检查是否至少匹配一个元素
noneMatch() 检查是否没有匹配所有元素
findFirst() 返回第一个元素
findAny() 返回当前流中的任意元素
count() 返回流中元素的总个数
max() 返回流中最大值
min() 返回流中最小值
2 查找与匹配
map ->reduce加工 ->计算结果
3 规约 reduce
一类是对Collector中的数据进行操作或者计算
counting()方法(计算数据个数)或averagingDouble(ToDoubleFunction<? super T> mapper)方法(计算容器中数据的平均值)
一类是将Collector中的数据另存到一个容器中
存到List中则调用toList()方法
存到Set中则调用toSet()方法
因为Map对象中的key无法重复,放入该Map对象的key数据也不可以重复,如果重复就会报错IllegalStateException,即要保证转为Map对象的时候key值不可以重复(如果是平时的Map对象,需要调用put()方法来向容器中添加元素,但是这里是通过toMap()方法直接将该容器转为Map容器,所以key的数据不可以重复)
存到Map中则调用toMap()方法conllectors.toMap
还有counting,mapping,maxbyminby,filltreing等等
Collectors的方法分为两类
collect()
4 收集
3 终止行为
步骤
Stream
开源框架常用,reflection,在运行中,对任意一个类都能知道该类的所有方法与属性(发生在程序运行期间),java中唯一一个动态加载机制
定义
java反射机制内容:(功能)1.在运行时判断任意一个对象所属的类;2.在运行时构造任意一个类的对象3.在运行时判断任意一个类所具有的成员变量与方法4.在运行时调用任意一个类的方法5.动态代理生成
作用
反射的源头:class类实例表示正在运行的java应用程序的类与接口
注意:class对象在类加载到内存时就会存在,唯一只有一个,包含类中所有的内容,只要获取class对象,就可以操作类中的内容
内容
获取class对象:范反射源头1.类名.class2.class.forName(权限定类"包名.类名")-->推荐_>更灵活3.对象.getClass()4.getSuperClass()5.基本数据类型.class==Integer.Type
能获取构造器,注解,接口等等供框架使用,方法修饰符,数组,枚举,注解,基本数据类型等
反射操作构造器|创建对象1.newInstance()默认调用空构造,在创建对象的同时对对象,初始化信息---》已过时2.先获取构造器,然后指定在创建对象时使用哪一个构造器\tconstructor:\t\tgetConstructor()\t\tgetConstructors()\t\t以上获取公共构造函数\t\tgetDeclaredConstructor()\t\tgetDeclaredConstructors()\t\t获取所有构造函数使用:私有构造器要先忽略权限con.setAccessible(true)con.newInstance()con.setAccessible(false)
反射操作方法\t1.获取方法\t\tgetDeclaredMethods\t\tgetMethods\t2.使用方法\t\tinvoke(对象,参数)\t\t或invoke(null(指的是类),参数)
反射
注解
xml
正则表达式
对维护的代码关闭修改,允许扩展代码
目的:防止代码出现问题,导致业务功能无法使用
开闭原则
单个实例叫做单例。在业务中,需要当前类有且只能创建一个对象。此时,我们可以通过单例模式实现。单例模式的实现方式有5种。设计模式是不单独出现的
优点:天然线程安全
缺点:不能延迟加载
Tips:如果该种单例类种存在一个其他的静态方法,外界调用该方法会导致该种单例类被加载到内存中,而加载过程又会触发static加载(有且只做一次),静态方法进内存之后,等待调用,静态属性进内存之后,会触发赋值操作(如果没有初始化,赋值为默认值,如果初始化了,则将初始化的值赋给静态变量)。即只要调用其他静态方法,无论是否需要这个单例类对象,都会在内存中自动加载一个该类的对象。所以缺点就是占用一定内存资源。
饿汉式单例
特点:支持延迟加载,但线程不安全
懒汉式单例
单例模式
模板方法
策略模式
设计模式
其他
数据库
产生原因:Java传统上而言,是一门编译型语言
实时编译和预编译
解决方法:采用JIT与AOT来提升整体运行速度
Java如何解决慢的问题
Java的跨平台(write once run anywhere)
自带图形化界面
自带命令行界面DOS
windows
Mac
蝶变:UBT
图形化界面
RedHat:CentorOs
命令行界面终端
Linux
操作系统
Dos命令寻址
环境变量逻辑
一个文件中可以有多个类,但只能有一个类被public修饰符修饰,称为主类。若一个文件中有两个类,则编译的时候会有两个字节码文件
类名即为文件名
一个类中只能有一个main方法
Java的基础规则:
Java预备知识
Java要求不能以数字开头,以字母,下划线,美元符开头,后跟字母,美元符,下划线,数字结尾
Java中的标识符不能为关键字
Java严格区分大小写
三个准则
驼峰原则
见名知意
两个标准
标识符
关键字
字符集合
注释
变量名称
对于变量而言存在数据类型,对于数据本身而言也存在数据类型
Java是一门强类型语言
变量类型
作用范围
变量
数据类型:Byte 字节型
所占空间:1B
注意:赋值的时候,不能超过byte类型的范围,否则会报错:int转byte会有所损失
Byte
数据类型:Short 短整型
所占空间:2B
注意:赋值的时候,不能超过Short类型的范围,否则会报错:short转byte会有所损失
Short
数据类型:int 整型
所占空间:4B
作用范围:(-2^31-2^31-1)
int
数据类型:long 长整型
所占空间:8B
作用范围:(-2^63-2^63-1)
注意:赋值的时候如果将一个超过了int范围的字面值赋予long类型的变量,需要在整个字面值后面加l或者L
long
数据类型:float 单精度
作用范围:小数点后6-7位,前6位绝对精准,7位部分精准
注意:把一个字面小数赋值给float变量的时候,需要在后面加F或者f
float
数据类型:double 双精度
作用范围:小数点后15-17位,前15位绝对精准,17位部分精准
注意:不可以用小数进行运算,可以通过扩大倍数计算再缩小到原位数来达到计算目的
double
在Java中所有的整数的字面值默认类型都是int类型
计算机中的小数是有穷尽的。在计算机中可以发生等概率事件的
所有的小数默认数值类型都是double类型
数值型数据类型需注意:
数值型
数据类型:char 字符型
取值:用单引号引起来,可表示65535个字符
char ch='a';(直接输入字符)
char ch='\\u56CE';(\\u后加一个四位的十六进制)
a=97
A=63
表达方式:
字符的相加是ASCII码的相加,最后的结果默认是int除非用这样子的代码将原字符每个都转为字符串,否则就是字符串的相加
char
字符型
数据类型:逻辑型 boolean
所占空间:1字节(本来只需要1位的空间即可,但是由于java语言的要求,内存中开辟的空间必须是8的整数次幂,所以实际占用了8位即1字节的空间)
注意:true和false是关键字
boolean
逻辑型
自动转换类型 小变大 自动转 看所占空间 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(expression){// 表达式 boolean值 //loop-statment 循环语句}
语法结构
判定表达式的值,如果表达式的值是true,执行循环体一次
继续判定表达式的值,直到表达式的值是false,则跳过整个while循环执行后续代码
执行顺序
尽量防止写成死循环(死循环是有意义的)
注意事项
while循环
do{ //loop-statment}while(expression);
先执行循环体一次
再判断表达式的值,如果表达式的值是true执行循环体一次
直到表达式的值是false,则跳过整个while循环执行后续代码
先做一次,然后再判定
do-while循环
for(循环变量声明;循环条件;){ 循环体;}
先声明循环变量
执行i++操作
继续判定表达式的值,直到表达式的值是false 则跳过整个for循环执行后续代码
while : 确定循环终止条件
for: 能够确定循环次数
计算表达式的值
依次匹配对应的case 如果匹配上了之后 执行对应的执行语句,结束整个switch分支
如果所有的case都不满足 则执行最后的default
jdk1.5之后支持enum枚举类型
jdk1.7之后支持String类型
switch表达式中能够产生的结果只能是:
switch中的case穿透问题 需要通过break来防止穿透,也可以利用case穿透的特点来简化看法
switch分支
判定表达式的值
如果表达式的值是true 则执行执行语句
如果表达式的值是false 则跳过if 执行后续代码
单分支相当于只开了一条分支,在某些情况下可能不太够用
单分支结构
如果表达式的值是true 则执行执行语句1
如果表达式的值是false 则执行执行语句2
双分支结构中执行语句一定是互斥的,所以一定会保证有一个是一定被执行的
双分支结构
判定表达式1的值
如果表达式1的值是true 则执行执行语句1 结束整个多分支
如果表达式1的值是fasle 判定表达式2的值 以此类推
如果所有的表达式都不满足 执行最后一个else对应的执行语句
多分支结构中分支越多可能程序会变慢,多分支中分支过多会造成阅读体验感变差,维护性降低
多分支结构
if分支结构
break continue语句
continue语句在循环句体中,用于终止某次循环过程,跳过循环体中尚未执行的语句,接着进行下一次是否执行循环判定的判定
continue语句
在任何循环语句的主体部分,均可用break控制循环的流程。break用于强行终止整个循环,不执行循环中剩余的语句(break语句还可用于多支语句switch中)
注意:break用于终止整个循环
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不同:参数列表不同[个数,顺序,类型]
方法重载
重载调用满足的条件:最近最优
完全匹配>自动类型转换(看变量的转换次数)
重载的面试题
方法签名由方法名+形参列表构成
方法签名的意义:方法名和形参数据类型列表可以唯一的确定一个方法,与方法的返回值一点关系都没有,这是判断重载重要依据
方法签名
***方法
程序运行期间其值不可以发生改变
符号常量:通过final修饰的变量称之为常量 常量在程序运行期间,值不可以发生改变
常量分为
全部大写,然后单词之间通过_分割 例:YOU_JI_XIAN_XUE_TANG
符号常量的标识符
拓展:常量的编译速度很快
常量
在方法中,程序调用自身的编程技巧称之为递归
关于递归:运行效率慢,吃内存。一般在企业开发中用不到
循环的效率远高于递归
将一些重复计算的中间结果 暂存起来
使用尾递归(不是所有的递归都可以写尾递归)
递归解决效率慢的方式:
递归
其他知识点
基础内容
数组是一个存储相同类型数据的,开辟连续的存储空间有序集合(一组数)
第一种静态初始化
第二种静态初始化(一般不用)
静态初始化
动态初始化
数组的初始化
数组中的数据可以自动向上转型
整数为0
小数为0.0
布尔值为false
char值为空格
引用型为null
1 开空间,在堆内存中开辟一份空间出来,赋默认值
2 构造器初始化 ,给数组的每个索引位置上的元素进行初始化
3指向引用,将堆内存中的地址赋值给局部变量进行存储
以后只要看到new关键词(栈中的对象名指向堆中的地址)
内存情况
基本数据类型存储在栈中
常用属性:索引、长度
只要学习存储数据的集合,就考虑这五种操作:增删改查+迭代
数组的常用属性
数组的迭代即将数组中的元素 依次拿出来瞅一眼
通过普通for循环以及索引来达到迭代(这种方式的迭代可以对每个索引下的值进行操作)
通过增强for循环(foreach循环)来直接迭代
数组的迭代
二维数组的声明
内层每一个一维数组的长度都是固定的,外层二维数组与内层所有一位数组都已创建
数据类型[][] 数组名 = new 数据类型[外层二维的长度][内层一维长度];
外层二维数组已创建,内层的所有一维数组没有创建,在能够确定长度或者数据的时候再根据需求创建
数据类型[][] 数组名 = new 数据类型[外层二维的长度][];数组名[外层二维索引] = 一维数组创建方式;
二维数组的初始化
操作二维数组中的数据根据索引来操作:数组名[二维索引][一维索引]
二维数组的遍历:for或者foreach都可以遍历
小拓展:二维数组
main方法中的参数默认情况下,被java解释器调用之后,传入的应该是一个空数组对象。可以通过配置来传入具体的参数
java解释器 一定给args传参数
java解释器在执行main方法的时候传入了一个数组对象 这个对象默认情况下长度为0(不是该对象为null,如果该对象为null就不会在堆内存中开空间,即不可能获得长度)
但是该数组对象一定占用类内存空间(因为new了就要开空间)
关于main方法中的参数问题
JDK1.5之后支持形式参数为可变参数
可变参数可以接受任意长度的固定数据类型的调用,但是必须要保证方法的参数列表中有且只能有一个可变参数并且这个可变参数一定在末尾
可变参数本质上就是一个数组,只不过编译器会自动帮助我们去构建该数组
传入可变参数中的实参个数可以为0个,可以为1个,可以为多个
有可变参数方法的声明:
可变参数问题
1 我们可以通过计算的方式快速地定位指定索引上的元素地址。(索引位置的地址=首地址+(索引)*数据长度)所以数组通过索引去获取元素效率是特别高的
2 数组的问题就是不能增加和删除。如果要添加和删除,其实就是在新数组上操作的,根本无法在原数组上进行,所以效率特别差
数组最大的问题:数组的长度一旦指定无法修改
3 如果在使用场景中,大量的出现了添加和删除,不建议使用数组进行操作,如果有高频次的查询操作,并且可以将业务中的模型和索引进行映射关系处理,那么优先使用数组
为什么数组的索引是从0开始的?请说明一下数组的优缺点。请说明一下你们公司中哪些业务场景使用了数组(这个问题面试的时候需要仔细询问具体环境,相当于面试中的一个坑)
面试问题
冒泡排序
数组的边角知识
参数类型相同,参数个数可以从0到多个
是jdk1.5之后的特性
格式:数据类型... args 例如String ... args
注意点:方法参数要有多个参数要把可变参数放在后边这样可限定至少求一个整数的和java内部会为可变参数构件数组但数组不能转为可变参数
可变参数
都来自于System.arraycopy(数组1,起始位置,数组2,起始位置,复制的长度)方法在java.util.arrays中
copy()
fill()
\t\t\t\t\t\tstatic void sort(Object[] a) 默认对数组中的数据做升序排序,如果A对应的类继承了Comparable并且重写了compareTo方法,那么sort排序将按照compareTo方法体中的逻辑进行排序字符根据unicode字符串的比较通过首字母比较和后续的字母通过Unicode比较
该方法要求参数数组必须升序排序。
如果数据不存在,则返回 -插入点的索引-1
如果查询的数据比数组中的所有数据都小,那么返回值将会是-1即(-插入位置-1)=(-0-1)=-1
toString()获取数组的内容,不是重写是重载方法
asList(T... a)方法:将数组转成列表
二维数组的内容或者俩数组比较DeepToString()DeepEquals()DeepHashcode()
Array工具类
大概了解,再次遇到时,像查字典一样去查找,使用
API的使用
数组
***Java基础知识
\t面向过程:可以处理一些简单的事情,但是复杂的事情,通过面向过程就显得比较不舒服。适合处理结构化问题。通过先设计框架,然后拆分,一步步的去解决,然后组合起来来解决一个具体的问题
面向对象:做简单的事情就是大材小用,但是处理复杂的业务就更加的得心应手一点
面向过程和面向对象不是对立的
语言的分类
对象:具备一定能力的个体。在java中万事万物皆为对象
先有类 再有对象:编写代码角度,先有类,再有对象,对象是通过类产生的
先有对象再有类:构建角度,所有对象的共性勾勒出来,定义为一个类
先有类还是先有对象?
对象和类的关系
构造器的作用:为对象初始化信息
方法名称和当前类是同名的
这个方法不需要编写返回值
这个方法内部不需要编写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 .....继承链过长会导致后期的维护成本增加
继承关系是类和类的强关联关系,即类之间的耦合性会增加,但编写代码提倡的是低耦合,关联度越高,后期的维护成本越高
缺点
继承的优缺点分析
继承的使用
若this()与super()在一个构造器中顺序排列会报错,因为这两个都要争抢第一行,就会报错,但是每个构造器中都会默认调用super(),故删掉super(),再写this()就没事,最后调用顺序依然是super()第一个加载后续是this()
专门调用父类的构造器,保证子类创建对象的时候,父类已经存在。默认会在构造器的首行
默认情况下存在一个super()调用父类的空构造器
如果子类没有构造器的互相调用,那么子类的每个构造器首行都存在一个super()执行父类构造器
super()
除了不可省略,剩余全部可以省略
子类调用父类的属性或者方法时不可省略
super.
super关键字
子类对象,区分局部和成员变量在构造器首行调用this()调用构造器
this
创建顺序是先静态后代码块后成员(先父类后子类)不能同时存在,必须都在方法第一行可以区分同名问题,无同名时可以直接使用都是对象,无法在static里面使用
父类对象,在构造器首行通过super()调用其他构造器在首行不指定会默认写super()
super
this与super关键字的区分
老祖宗类,java中所有类的父类,java所有类都会直接或间接继承自object类一个类如果没有显式的继承自其他类,则默认继承自object类
输出语句在输出一个对象的时候,会默认调用当前对象的toString方法
Object的toString方法打印输出当前类的全限定名(当前类的包名,类名)+@+当前对象的由hash值转换得到的16进制数
一个对象的hash值就是一个唯一标识,所以可以将其看作是当前对象的地址
System.out.println的含义
toString()
比较两个引用类型的对象是否相等
==可以比较引用类型的,但是比较的是两个引用类型的地址。如果两个对象是new出来得到的结果一定是false因为所有的类都是Object的子类,所以所有的类都拥有一个equals方法,如果该类中没有重写equals方法,那么就会调用Object类中的equals方法,而且Object的equals中是通过==比较的
但是如果两个用户的属性都相同,便认为是同一个对象。Object中的equals方法不满足子类的要求,便需要重写
equals()
Object
一定要存在继承关系
子类中编写了和父类中同名的方法
剩余的修饰符、返回值类型、参数列表一定要一模一样
基本数据类型要完全一致,如果是引用数据类型,则要小于原方法的类型
被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把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异, 写出通用的代码,做出通用的编程,以适应需求的不断变化
多态
实例:开发部门的java工程师和web工程师都有work这个行为和一些其他共同的行为和属性,但是不清楚这个行为具体怎么做,于是可以抽象出来一个类,这个类有一些子类共同的方法和属性
abstract关键字可以修饰类和方法。修饰的类称之为抽象类、修饰的方法称之为抽象方法
抽象类是不能实例化的,也就是说抽象类是不能创建对象的。但是可以定义构造器,因为这个构造器是给子类用的
native(本地方法,看不到方法体例如public native hashcode()
native
抽象类可以实现接口,但不能继承接口,可以部分实现接口,这样为开发者提供相当的便利性,这个抽象类称为便利类
1 完成了多态的先决条件
2 避免了子类的随意设计
抽象类的意义
抽象类
通过abstract修饰的方法称之为抽象方法,抽象方法指定以方法的声明,不需要编写方法体
语法: 修饰符 abstract {void|数据类型} 方法名([参数列表]);
抽象方法只能被定义在抽象类中,不能在普通类中定义,将类与接口分离开的话,拥有抽象方法的类一定是抽象类
抽象方法无法被static修饰,因为如果被static修饰了的话,则可以通过类名来直接调用该方法,但是抽象方法是没有方法体的,所以无法被static修饰
抽象类中的抽象方法只能被public或者protected修饰
抽象方法
要么将父类中所有的抽象方法全部实现
要么自己也是个抽象类
一个类继承了抽象类后必须要做决定
abstract关键词
接口是特殊的抽象类,是一种更规范,比抽象类更抽象的类
接口声明的时候,需要定义为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调用static内容 直接调用
静态内容隶属于类,类存在静态内容就存在,非静态内容隶属于对象,对象存在,非静态内容才能存在
静态方法属于类,所以父类或者接口中的静态方法无法被重写,即子类或者实现类无法获得父类或者接口中的静态方法
类的静态方法表示类的行为,类的实例方法表示类对象的行为
类变量与static变量的区别
内存空间
static关键词
java值传递:基本数据类型是数据值传递,引用数据类型是地址值
最后的,最终的(成员,局部变量都能修饰),被final修饰的变量为常量,修饰的方法不能被重写,类不能被继承(变为太监类)
final不能修饰抽象类,抽象类就是为了继承然后让子类重写方法,如果被final修饰了的话就无法被继承,即抽象类将毫无意义
final修饰的类是用来防止继承
final修饰的方法就是用来防止重写的
final关键词
过程
new关键字
1 开空间 在堆内存中开辟一份空间出来
2 初始化 给类中的成员变量进行初始化赋默认值
3 指向引用 将堆内存中的地址赋给局部变量进行存储
内存分析
类是公共的
属性私有化
私有属性的公共的访问方式
至少一个空构造器
重写toString方法
重写equals方法
Javabean类:泛指一系列类的统称--->实体类
Mvc
类中定义类,具有类的特点,可以继承,实现接口,类中定义成员等
成员特点可以被修饰符修饰,于是private是私有内部类,static是静态内部类,局部位置是局部内部类,匿名后是匿名内部类,1.8后可通过lambda表达式使用
概念
注意点:除了静态常量能被定义,静态内容需定义在静态内部类中内部类可以使用外部类私有和静态内容,外部类可以通过new对象来使用内部类的成员(私有也行)
内部类使用外部类:直接使用,因为内部类中都持有一个外部类对象的引用:外部类.this
外部类使用内部类:直接使用,创建内部类对象,通过对象调用
其他类使用内部类:或者先创建外部类对象在创建内部类对象
成员内部类
多了:私有内部类其他类无法访问,只能所在的外部类通过创建对象使用
私有内部类
多了一层静态的含义
假设推理法,演绎推理法编译器看,语法是否合乎习惯,是否合乎规范,是否合乎语法等
多了一个static,就是可以用静态语法,类名.静态内容。用成员语法,通过new对象来访问内存创建顺序不同外部类可以随意使用内部类,这个是常识
静态内部类
定义在方法中的类称之为局部内部类。随着方法的执行而开启,方法的结束而销毁。开启与销毁指的是类的生命周期
可定义非静态成员内容,除了静态常量。使用所在方法的局部变量,这个变量默认被final修饰,是因为gc堆栈和作用域机制,为了保证数据一致性jdk1.8之后,默认被final修饰,1.7及之前需手动
局部内部类可使用外部类成员(关注是否为静态内容)
依旧遵守类成员,静态内容,语法,多了一个局部,其他地方都用不了,只能在方法体内使用
只想在方法内使用生存周期只在方法内
以后也不会被用到。降低了内存消耗,让业务更加具备面向对象的能力。在业务中抽取了一些共性,让代码编写更加优雅。
局部内部类
是什么:用来简化符合要求的子类或实现类对象,他没有自己的本身作用,实现接口重写方法,只在当前使用一下
为什么:就是只能使用一次的类称之为匿名内部类,快捷键生成。\t一般我们用这个匿名内部类来接口的实现类对象和抽象类的子类对象
全称:匿名内部类对象-》实现类对象,子类对象
怎么做:new一个接口类实现类子类对象+类体{};重写方法使用方式:1.在当前使用一次:new inteface(){重写内容}.test();\t2.使用多次:将对象赋值给,引用,记录地址,来多次使用\t3.在方法实参使用,将对象实参赋值给方法的形参,使用方法时在()里面new对象
匿名内部类
是什么:简化符合要求的匿名内部类
前提:满足函数式接口(jdk1.8之后特性),即只有一个可被重写的抽象方法,通过@FunctionInterface检查一个接口是否为函数式接口
lambda表达式结构()->{}():重写的函数的参数列表->:lambda表达式的符号,具有上下文推导作用{}:要重写的方法的方法体
标准写法:Swim s=(参数列表)->{方法体};写法2:方法体一句,省略{}写法3:参数列表的参数类型可省略写法4:1个参数时可省略()写法5:如果方法体有且只有一个返回语句,则{}与return都可省略
简单抽象,后期与流式编程一起使用
简化匿名内部类对象
前提要求
() ->{}
() 要重写的抽象方法的参数列表
{} 重写的方法体
->lambda符号,箭头函数
语法
() ->{方法体语句}
写法一
当lambda体中语句只有一句的时候,前后的{}可以省略
()->System.out.println("快乐学习");
写法二
在lambda体中语句只有一句的时候,并且如果存在参数,lambda结构中参数的数据类型可以省略
写法三
在写法三的条件下,参数只有一个,参数前后的()可以省略
x->System.out.println(x);
写法四
如果lambda体只有一句,并且这一句为return语句,则前后的{}与return关键字可以一起省略
x->x>0;
写法五
lambda表达式写法
定义: 内部只有一个必须被重写的抽象方法
java.util.function包下提供了一系列的函数式接口
检查: 通过注解@FunctionalInterface
Consumer<T>{ void accept(T t);}
消费型接口(需要参数,无返回值)
函数型接口(需要参数,有返回值)
Supplier<T>{ T get();}
供给型接口(无需参数,有返回值)
Predicate<T>{ boolean test(T t);}
段言型接口(需要参数,返回值为布尔型)
作用: 简化lambda表达式,是lambda表达式的另一种表现形式
前提:lambda表达式的实现,已经有其他方法提供了实现,可以直接调用那种已经实现的方法,即可以直接通过方法引用去引用那个方法
1 lambda体是否是通过调用另一个方法实现的
2 内部所引用的方法的参数列表与返回值要求与lambda表达式的参数列表与返回值是否可以保持一致,一一对应
3 内部所引用方法的返回值要求对应lambda的返回值,lambda参数列表的第一个参数作为内部调用方法的对象存在,lambda参数列表的第二个参数往后,能够一一对应内部所引用方法的参数列表
分析步骤要求
方法引用-->::
对象::实例方法
类名::静态方法
类名::实例方法
lambda可以将行为作为参数进行传递(行为:功能的实现,操作方式,步骤)
Lambda表达式
1.内部类提供了某种进入其外围类的窗口。
2.每个内部类都能独立的继承自一个(接口的)实现,所以无论外围类是否已经
继承了某个(接口)的实现,对应内部类都没有影响。
3.内部类使得多重继承的解决方案更加完整。虽然接口解决可部分问题,
但是内部类有效的实现了多重继承。也就是说:内部类允许继承多个非接口
类型(类或者抽象类)。 我认为这是内部类最重要的一个作用。
4.内部类可以有多个实例,每个实例有自己的状态信息,与外围类相互独立。
5.单个外围类中,可以让多个内部类以不同的方式实现统一接口,或者继承 同一个类。
6.创建内部类的对象并不依赖外围类对象的创建。
7.内部类没有令人迷惑的is-a的关系,是独立的实体。
作用和特点
内部类作用
内部类(了解,理解)
面向对象*******
是什么:程序无法正常执行,遇到异常,需要去处理
什么是异常
一般为虚拟机生成并脱出的,不由程序控制
Error
由虚拟机监控到的一些可控的异常,程序员需要通过异常的提示信息进行修改程序,解决问题。Exception分为两大类异常,一个是RuntimeException,另一个是CheckedException
运行时异常,即程序运行期间发生的异常,可以通过增强程序的健壮性处理如:1.添加if语句进行多种条件的判断,来达到尽可能的在各种条件下都能运行。\t\t\t\t2.或者异常解决方案来解决,即报错
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")
常见的运行时异常:
RuntimeException
检查时异常 | 编译时异常 ->程序编译期间发生的异常,检查语法
这种异常只能通过异常方案处理(如果检查时异常不处理,程序无法运行)
CheckedException
Exception
Throwable
异常的体系分类
方法签名上throws 异常,方法体throw,抛出到上一层,使用者处理(从方法内部抛出到方法上,由调用方法者处理)
重写方法对异常抛出的要求: 重写方法上抛出的异常类型 < = 被重写方法上异常的抛出类型
异常抛出:
一个try后面可以跟着1~N个catch,多个catch中,接收明确的异常对象的catch放在前面,接收大范围类型异常对象的catch需要放在后面
执行try中的代码段,如果整个代码段中没有出现异常,则不执行catch,直到结束try..catch结构,然后最后执行finally语句
finally无论如何都会执行,除非虚拟机中断,即使catch中有return,finally也会去执行
如果所有的catch中的异常种类都不满足,JVM会中断程序的执行
try...catch...finally流程:
异常捕获:
异常处理方案
自定义的异常类型为运行时异常,要求直接或者间接的继承RuntimeException,编译时异常是继承自Exception
throw 制造异常,throws在方法签名上,可构造器重载,调用父类方法
语法:throw new 异常名();
自定义异常
异常
\tobject类:老祖宗类,是java中所有的类的父类,java中所有的类都是间接或直接继承自object类的,如果一个类没有显式的继承其他类,则会默认继承object类
hashcode()
返回对象的字符串表现形式:包名.类名@+十六进制的整数=getclass().getName()+"@"+Integer.toHexString(hashcode())
(健壮性)原比较地址值(对象类型),通过重写后先比较地址值再比较内容
jdk包内很多都重写了equals方法,例如string类
clone()
getClass()
object类
Arrays类
static double abs(double a) :返回double类型的绝对值
static double ceil(double a) :返回大于或等于参数的最小整数(向上取整)
static double floor(double a) :返回小于或等于参数的最大整数(向下取整)
static long round(double a):返回与参数最接近的long,判断四舍五入是根据小数点后一位
static double sqrt(double a) :返回double值得正确舍入平方根
Math.PI 数学π
Math类
通过enum关键字定义枚举类型
描述一种事物的所有可能或一个类型的所有实例
使用可根据类名.静态实例来使用
自定义的枚举类型默认是隐式继承自java.lang.Enum,他是公共基类
name()返回字段名,ordinal()返回字段序数,从零开始,values()获取所有实例字段,switch在jdk1.5后可以使用枚举类型字段来判断
通过枚举类的字段(也就是实例)提供当前枚举类型的实例,默认的被public static final修饰
枚举类型中的构造器默认私有化
枚举类中可以定义成员(即实例),成员变量,构造器,方法,根据需要定义属性功能构造器等一般是直接定义字段,直接拿枚举类型字段实例的地址去直接比较,不用去赋值,这样更简单
枚举类型
String类表示字符串。 Java程序中的所有字符串文字(例如"abc" )都实现为此类的实例在源码中jdk11 下,是 private final byte[] value; jdk8下private final char[] value;
字符串翻转
可能考的题
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(byte[] bytes) :通过使用平台的默认字符集解码指定的字节数组构造新的 String(utf-8编码中,1个汉字对应3个字节;GBK编码中,1个汉字对应2个字节)
String("对应的字符串") :初始化新创建的String对象,使其表示与参数相同的字符序列; 换句话说,新创建的字符串是参数字符串的副本
构造器**
**char charAt(int index) : 返回指定索引处的 char值
String concat(String str) :将指定的字符串连接到此字符串的末尾并返回新的字符串
*copyValueOf()将字符数组转为字符串
*getBytes()返回字节数组,可带类型gbk utf-8等等
*****equals()判断两个字符串内容是否一致
***indexOf()返回字符或字符串第一次出现的索引位置,可设置起始offset位置
***lastIndexOf返回最后一次出现的位置
*****length()返回当前字符串的长度
***replace(老字符串,新字符串)用新去替代老的,可用正则表达式replaceAll()同理
*repeat(n)将一个字符串重复n次连接起来并返回
**boolean contains(CharSequence s): 当且仅当此字符串包含指定的char值序列时,才返回true
***String[] split(String regex): 将此字符串拆分为给定regular expression的匹配项返回字符串数组
**char[] toCharArray(): 将此字符串转换为新的字符数组
***String strip()与String trim() 这两个方法均可以将原字符串中前后的空格符删除,但是trim方法只可以删除半角空格无法删除全角空格。strip方法既可以删除半角空格也可以删除全角空格
**toLowerCase(),toUppercase将字符串字母变大小写
***String.valueOf()返回各种类型,基本数据类型以字符串表示的形式
String类*****
StringBuffer和StringBuilder都是为可变字符序列
都是字符序列,实现相同的CharSequence接口,都表示字符串
底层都是通过字节数组存储字符串中的数据(老版本jdk使用字符数组)
共同点:
String 为不可变的字符序列
StringBuilder 可变字符序列
StringBuffer 可变字符序列
是否可变:
StringBuilder>StringBuffer>String
执行效率:
StringBuilder 线程不安全 | 不同步
通过synchronized同步锁控制安全
StringBuffer 线程安全 | 同步
线程安全 | 同步:
不同点:
区别:
一个可变的字符序列。 此类提供与StringBuffer兼容的API,但不保证同步。 此类设计用作StringBuffer替代品,用于单个线程使用字符串缓冲区的位置(通常情况下)在可能的情况下,建议使用此类优先于StringBuffer因为在大多数实现中它会更快
StringBuilder:
以后某些类型结构中,如果底层存储数据的结构式数组,一般都会存在初始容量,初始容量指的是数组最初始的长度容量
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
delete(int begin,int end)删除指定范围内的字符串
insert(int offset,String str)在指定索引位置插入字符串
reverse()翻转字符串并返回
方法***
StringBuffer类与StringBuilder类*****
byte的包装类为Byteshort的包装类为Shortint的包装类为Intlong的包装类为Longdouble的包装类为Doublefloat的包装类为Floatchar的包装类为Characterboolean的包装类为Boolean
jdk1.5新特性--->自动拆箱装箱装箱: 基本--->引用拆箱: 引用--->拆箱
2包装类中封装了一些成员,方法,方便使用,灵活,功能强大
3引用类型的属性 | 字段,可以区分比较,比如是否新用户 | 已经消费为0的两种状态,null为还没充钱,0为充钱了但是花光了
有了基本类型为什么还要提供包装类?
1使用方便,简单
2有利于节省内存
为什么有了包装类还要保留基本数据类型?
1 如果两个基本数据类型比较,值相等就相等
2如果两个new Integer比较,肯定不相等,因为new是开辟新的对象地址
3如果是int与Integer比较,无论是自动装箱还是new Integer,只要值相等就相等,因为会发生自动拆箱,然后再比较基本类型数据值
Integer类对象比较问题:在表示的数据值相同的情况下,用==进行比较
Integer.valueOf()装箱String.valueOf()转为字符串in.intValue()拆箱
max().min()
api
包装类*****
jdk8之前就提供--->Datejdk8 新增
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):测试此日期是否在指定日期之前
Date类:Date类表示特定的时刻,精度为毫秒
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"
日期和时间模式
简介
日期时间相关类
常用类****
File(String pathname) 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例(重要)
boolean canWrite() 测试应用程序是否可以修改此抽象路径名表示的文件
setReadOnly()
boolean delete() 删除该file对象指定的文件夹或者文件
boolean createNewFile() 当且仅当具有此名称的文件尚不存在时,以原子方式创建由此抽象路径名命名的新空文件
boolean mkdir() 创建此抽象路径名指定的目录(只能创建一层目录)
boolean mkdirs() 创建此抽象路径名指定的目录,包括任何必需但不存在的父目录
getAbsolutePath()获取绝对路径,默认放在项目根目录下
lastModifier()返回最后一次修改时间
length()返回文件大小长度
renameTo()重命名
IO前知识:File类
输入流
输出流
根据流向分
字节流:万能流
字符流:只能操作纯文本内容
根据操作单原划分
节点流:真实操作数据,从数据源到目的地之间传输
功能流(包装流):从数据源经过包装再到目的地---->增强节点流的功能,提高节点流的性能
按照功能分
流的分类
此抽象类是表示输入字节流的所有类的超类。需要创建的是具体子类的对象,实现数据的传输
数据源为文件:FileInputStream 文件字节输入流
数据源为字节数组:ByteArrayInputStream 字节数组输入流
根据数据源的不同选择不同的子类: ---->节点流
InputStream字节输入流
此抽象类是表示输出字节流的所有类的超类。需要创建的是具体子类的对象,实现数据的传输
数据源为文件:FileOutputStream 文件字节输出流
数据源为字节数组:ByteArrayOutputStream 字节数组输出流
根据数据源的不同选择不同的子类------>节点流
OutputStream字节输出流
fileInputStream.available()获取文件的大小,字节数
FileInputStream is=new FileInputStream(File)(String),报错fileNotFoundException
步骤:1.与文件建立联系\t2.创建字节输入流\t3.输入\t4.处理\t5.关闭
字节流*****
文件字符输入流FileReader:节点流 输入流 字符流无新增功能,可发生多态
Reader用于读取字符流的抽象类
文件字符输出流FileWriter:节点流 输出流 字符流 无新增功能,可发生多态
Writer用于写入字符流的抽象类
(只能操作纯文本文件)字符流
作用:加快读写效率
大概是都多加了一层包裹,其中文件字节输入输出流可以多态,文件字符输入输出流不能多态
字节输入缓冲流:BufferedInputStream 无新增方法,可以发生多态
字节输出缓冲流:BufferedOutputStream 无新增方法,可以发生多态
字节缓冲流
字符输入缓冲流:BufferedReader 新增方法:String readLine()读一行文字
字符输出缓冲流:BufferedWriter 新增方法:void newLine()写一行分隔符
字符缓冲流
缓冲流
Data流 | 基本数据类型流 | 传输数据+保留数据类型(基本数据类型+String)功能流---->字节流的功能流
Data输入流:DataInputStream 新增方法:readXxx()
Data输出流:DataOutputStream 新增方法:writeXxx()
读入的数据类型与写入的数据类型的顺序应当保持一致
读入的是写出的源文件(如果随便读入一个文件,那么就不知道数据类型的排列顺序了)
可以与缓冲流一起使用,用data包裹缓冲流,缓冲流包裹字节流
Data流
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类实现序列化接口\t2.setting设置\t3.Alt+Enter 快捷键
序列号
序列化*****
Object流***
别人写好的工具类
IoUtils:读写,拷贝,字节流,字符流
FileUtils:基于File,读写拷贝,比较文件
FilenameUtils:文件名不是对象,为了Unix与在Windows环境保持一致
FileSystemUtils(不常用)
常用\t
可以直接通过maven导入
或者1.指定位置下载2.在项目在创建lib,放置当前所要依赖的jar3.将jar加入 =add ad library4.直接使用即可
使用步骤
commons-io
IO流
IO流***
Java初级学习
0 条评论
回复 删除
下一页