内部类
*内部类的访问特点
*内部类可以直接访问外部类的成员,包括私有
*外部类要访问内部类的成员,必须创建对象
//内部类和外部类没有继承关系
*成员内部类怎么创建对象
*格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
*举例:Outer.Inner oi = new Outer().new Inner();
*局部内部类
*内部类在局部位置
*方法的内部 内部类的下面(因为代码在方法中是按顺序执行的)
*匿名内部类
*概念
匿名:没有名字
一个没有名字的内部类
*格式:
*格式:new 类名 ( ) { 重写方法 } //这里的类既可以是普通类也可以是抽象类
new 接口名 ( ) { 重写方法 }
举例: new Inter(){
@Override
public void method(){}
}
*匿名内部类的本质
本质:是一个继承了该类或者实现了该接口的子类匿名 "对象" //名字叫匿名内部类,但是真正得到的是一个对象
*匿名内部类的格式推导
匿名内部类是为了简化 "局部内部类"
*匿名内部类的转变过程
单独的实现类文件------------>内部类--------------->匿名内部类
*匿名内部类的使用场景
//特点是只能使用一次 + 本质是一个对象
当需要的类型是一个类、接口的时候,并且只使用一次
冒泡排序
*冒泡排序的原理
(1)每一轮比较下来能得到一个最大值
(2)一共需要比较n-1轮
(3)已经确定了位置的数,不需要再次进行比较
*代码实现
public class MaoPao {
public static void main(String[] args) {
int[] ints = {13, 85, 56, 32, 75};
for (int i = 0; i < ints.length - 1; i++) { //这里的-1是因为n个数据只需要比较n-1轮
for (int j = 0; j < ints.length - 1 - i; j++) { //这里的-1是因为为了防止数组越界 -i是已经确定了位置的数不需要再参与比较
if (ints[j] > ints[j + 1]) {
int temp = ints[j];
ints[j] = ints[j + 1];
ints[j + 1] = temp;
}
}
}
for (int i = 0; i < ints.length; i++) { //遍历元素
System.out.println(ints[i]);
}
}
}
Arrays数组工具类
用来操作数组的工具
*static String toString?(int[] a) 返回指定数组的内容的字符串表示形式。
//就是用来替代for循环遍历数组的
for (int i = 0; i < ints.length; i++) {
System.out.println(ints[i]);
}
等价于
System.out.println(Arrays.toString(ints));
*static void sort?(int[] a) 按照数字顺序排列指定的数组
//就是用来替代冒泡排序的
例如:Arrays.sort(ints);
*工具类
*工具类所在的包名必须使用util或者utils结尾
*工具类的类名必须以util或者utils结尾
*构造方法必须私有
*所有的方法必须是静态的
包装类
*将基本数据类型包装成一个类
*为什么要使用包装类
*可包装一些咱们记不住的数据
*为集合做铺垫 //主要功能
*基本类型和对应包装类
基本数据类型 对应的包装类
byte ------------ Byte
short ------------- Short
int -------------- Integer //注意
long -------------- Long
char -------------- Character //注意
boolean ----------- Boolean
float ------------ Float
double ------------ Double
*Integer类
*软件包 java.lang
使用的时候不需要导包
*继承体系
java.lang.Object
java.lang.Number
java.lang.Integer
*成员变量
static int MAX_VALUE 保持最大值一个 int可以有2 31 -1。
System.out.println(Integer.MAX_VALUE);
static int MIN_VALUE 持有最小值的常数为 int可以为-2 31 。
System.out.println(Integer.MIN_VALUE);
*构造方法
(1)Integer(int value) 已过时。
很少使用这个构造函数。 静态工厂valueOf(int)通常是一个更好的选择,因为它可能产生明显更好的空间和时间性能。
Integer i1 = new Intyeger(100);
(2)Integer(String s) 已过时。 将String转成Integer
很少使用这个构造函数。 使用parseInt(String)将字符串转换为int原语,或使用valueOf(String)将字符串转换为Integer对象。
参数String的要求是必须是纯数字的 NumberFormatException:数字转换异常
Integer i1 = new Intyeger("100");
重要:构造方法是可以进行数据类型转换的
*int和String的相互转换
*int------------>String
(1)使用int类型的数据+""
举例: String s1 = 100+"";
(2)String类的valueOf(int i)
static String valueOf?(int i) 返回 int参数的字符串表示形式。
举例:
String s = String.valueOf(100); //较为规范
*String------->int
(1)static int parseInt?(String s) 将字符串参数解析为带符号的十进制整数。
举例:
int i = Integer.parseInt("123");
日期类
*常用方法
(1)long getTime?() 返回自1970年1月1日以来,由 Date对象表示的00:00:00 GMT的毫秒数。
Date---------->毫秒值
返回值:指的是毫秒值
Date d2 = new Date();
long time = d2.getTime();
(2)void setTime?(long time) 将此 Date对象设置为1970年1月1日00:00:00 GMT后的 time毫秒的时间点。
毫秒值-------->Date类型
参数:毫秒值
Date d3 = new Date();
d3.setTime(1000*60*60);
System.out.println(d3);
*重点掌握Date与String的互相转换
*格式化方法
String format?(Date date) 将日期格式化成日期/时间字符串
代码实现:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format(Date date) 将日期格式化成日期/时间字符串
String format = sdf.format(d2);
System.out.println(format);
应用场景:转成前台页面想要的格式展示出来 好处:就是可以转成我们想要的格式
String ---------- > Date
*解析方法
Date parse?(String source) 从给定字符串的开始解析文本以生成日期。
代码实现:
Date parse = sdf.parse("2019-12-25 18:41:20");
System.out.println(parse);
应用场景:将String类型的时间字符串转成Date类型后,可以使用里面的getTime()得到毫秒值,做差值计算
异常
*Throwable 异常的根类
*Error 错误 / / / (内存溢出) / / / 栈内存溢出
*Exception 异常
*编译期异常(非RuntimeException)
*运行期异常(RuntimeException)
*处理方式
*try------catch
catch中的异常必须是try中代码运行会产生的异常,否则将无法进行捕捉
*try------catch-----finally
*throws 直接将异常进行抛出,暂时不进行处理
*编译时异常和运行时异常的区别
(1)编译时异常
*执行javac命令时出现的异常
*特点是在idea中会以红色波浪线的形式体现出来,不处理就无法执行
(2)运行时异常
*执行java命令时出现的异常
*特点是在idea中不会有红色波浪线的提示,只有在运行时才会出现
*为什么要区分编译期异常和运行期异常?
针对的角色不一样
1.运行期异常
针对于用户,也就是说运行期异常是由用户产生的。
但是用户产生的问题我们开发人员要去解决,给出一个友好的提示。
2.编译期异常
针对于开发人员
集合 (collection)
*常用方法
boolean add(E e) 添加元素 //重要 在末尾添加
boolean remove(Object o) 从集合中移除指定的元素 //重要 根据元素来删
//删除的是从0索引开始出现的第一个该元素
void clear() 清空集合中的元素 //高危操作
boolean contains(Object o) 判断集合中是否存在指定的元素 //很容易忘记
//弊端:虽然知道有没有这个元素,但是却不知道具体的位置
boolean isEmpty() 判断集合是否为空
int size() 集合的长度,也就是集合中元素的个数 //相对重要
*集合的判空
什么叫判空?判断该容器不为空
什么叫空? 这里的空有两层含义,一个是为null,集合的元素数量为0
if(null!=collection && collection.size()>0){
xxxxxxxxxxxxxxxxx
}
*length、length()、size()三者的区别?
length:数组的长度 是一个属性
length():字符串的长度 是一个方法
size():是集合的长度 是一个方法
*遍历
*为什么要使用迭代器而不是普通for循环来遍历Collection
因为Collection集合有一个子接口是Set,Set集合没有索引,所以使用普通for循环就不能得到每一个元素
*使用迭代器遍历元素的步骤
(1)获取迭代器对象 iterator
(2)循环获取每一个元素
hasNext()
//在这里有一个类似于指针的东西,初始位置在元素的最上方
判断有没有下一个元素
next()
*获取了下一个值
*取完值之后把指针向下移动一位
//注意:不用多次使用next()方法
*分类
*单列集合(list/set)
*双列集合(map)
list集合
*特点
*元素可重复
*存取有序
*有索引
*List集合的 "特有" 方法
void add(int index,E element) 在此集合中的指定位置插入指定的元素 //在指定的索引位置
E remove(int index) 删除指定索引处的元素,返回被删除的元素 //根据索引删除
E set(int index,E element) 修改指定索引处的元素,返回被修改的元素 //重要
E get(int index) 返回指定索引处的元素 //重要
//(1)List集合特有方法中有一个共同点,参数都有索引
/删除List集合中的指定元素
两种思想:
(1).新建一个集合,然后遍历要操作的集合,把符合要求的添加到新的集合中
(2).在原有的集合上进行遍历,把需要删除的元素直接删掉
//面试题:怎么删除List集合中指定元素?
"反向遍历"
*列表迭代器
(1)列表迭代器是List集合对象的
list.listIterator();
(2)列表迭代器的特有方法
*void add?(E e) 将指定的元素插入列表(可选操作)。
*boolean hasPrevious?() 如果此列表迭代器在相反方向遍历列表时具有更多元素,则返回 true 。
判断有没有前一个元素
*E previous?() 返回列表中的上一个元素,并向后移动光标位置。
获取上一个元素的值
将光标向上移动一位
注意事项:
(1)列表迭代器的add方法和List集合的add方法有什么区别?
*列表迭代器的add方法是在当前指针的后面添加
*集合的add方法是在末尾添加
(2)hasPrevious?和previous?的弊端
前提:需要先将指针正向遍历到末尾
原因:因为指针的默认初始化位置在元素的最上方
*增强for循环
(1)格式
for(元素的类型 变量名 : 数组或者集合对象){
xxxxx
}
(2)本质
底层代码其实就是迭代器,出现增强for循环的原因就是为了简化迭代器的格式
(3)注意事项
使用前要进行判空
List<Student> c1 = null;
for (Student stu : c1) { //会出现空指针
System.out.println(stu);
}
总结:List集合遍历的方式
第一种:迭代器遍历集合对象
第二种:普通for循环(因为list集合有索引) //需要修改集合中的元素
第三种:增强for循环 //遍历 不修改集合中的元素
*List集合的实现类的特点
ArrayList
底层结构是数组
查询快、增删慢
线程不安全的、效率高
LinkedList
底层结构是链表
查询慢、增删快
线程不安全的、效率高
Vector
底层结构是数组
查询快、增删慢
线程安全的、效率低
*总结:目前而言都用ArrayList
重点掌握:
增
删
改
遍历
判空
并发修改异常
并:并行 同时
发:发生
*条件一:并发:同时发生 (至少需要两个对象)
*条件二:修改:修改元素
//产生并发修改异常的条件就是上面两个同时发生
Iterator<Student> iterator = c.iterator();
while (iterator.hasNext()){
c.add(s1);
Student s = iterator.next();
System.out.println(s);
}
上面代码产生并发修改异常的原因?
条件一: 有两个对象同时在操作集合 一个对象是iterator,还有一个对象是集合对象c
条件二:集合对象这个方法c.add(s1),修改了集合中的元素
就产生了并发修改异常
Set集合
*特点
*唯一
数据不可重复
*没有索引
不能使用普通for循环进行遍历
*存取无序
*遍历
*迭代器
*增强for循环
*实现类HashSet集合的特点
*和Set集合的特点一毛一样
*底层结构是哈希表(数组+链表)
*Set集合是怎么保证元素唯一性的(为什么要重写hashCode和equals方法?)
依赖了hashCode和equals方法
(1)先计算hashCode值
*哈希表中还没有这个哈希值
直接将元素添加到set集合中
*哈希表中已经存在了这个哈希值
(2)将要添加的元素使用equals和该哈希值下面的“桶”中的每一个元素进行比较
*如果桶中已经有这个元素了,覆盖
*如果桶中没有,添加进来
*什么时候重写hashCode和equals方法?
*保证唯一性
*需要比较的是对象的属性
*LinkedHashSet
*底层结构是哈希表和链表组成
*特点
唯一: 由哈希表保证
存取有序:链表保证
*应用场景
去重
*TreeSet集合
*特点
(1)唯一
(2)有序(按照一定的规则排序) //注意
(3)不带索引
*排序方式
(1)自然排序
*要求:
第一个:创建TreeSet集合的时候,构造方法不能有参数
//TreeSet<Student> ts = new TreeSet<Student>();
第二个:创建TreeSet集合时的泛型类(就是尖括号里面的类TreeSet<Student>)
需要实现Comparable接口,重写comparaTo方法
public class Student implements Comparable<Student> {
public int compareTo(Student s) {
// return 0;
// return 1;
// return -1;
//按照年龄从小到大排序
int num = this.age - s.age;
// int num = s.age - this.age;
//年龄相同时,按照姓名的字母顺序排序
int num2 = num==0?this.name.compareTo(s.name):num;
return num2;
}
}
(2)比较器排序
*要求:
第一个:创建TreeSet集合的时候,构造方法要传入一个Comparator接口类型变量
//TreeSet<Student> ts = new TreeSet<Student>(Comparator对象);
//代码实现:
public class ComImpl1 implements Comparator<Student> { //传入的比较规则
@Override
public int compare(Student s1, Student s2) {
int num = s1.getAge()-s2.getAge();
if (num == 0){
int i = s1.getName().compareTo(s2.getName());
num = i;
}
return num;
}
}
public class Demo1 {
public static void main(String[] args) {
ComImpl1 c1 = new ComImpl1();
TreeSet<Student> ts = new TreeSet<>(c1);
Student s1 = new Student("王小二",12);
Student s2 = new Student("王小三",13);
Student s3 = new Student("王小四",12);
Student s4 = new Student("王小五",15);
Student s5 = new Student("王小六",13);
Student s6 = new Student("王小七",18);
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
System.out.println(ts);
}
}
(1)通用性
自然排序:
弊端:对于使用的是API自动帮我们重写的comparaTo方法,默认按照字典顺序,如果我们想改变排序规则是不能改代码的,比如String我们想要按照字符串的长度比较
比较器排序: //推荐使用
好处:在一定程度上解了耦合
TreeSet<Student> ts = new TreeSet<>(c1);
可变参数
*当我们在定义方法的时候,不知道有多少个参数的时候,可以考虑使用可变参数
*格式
修饰符 返回值 方法名(数据类型... 变量名){
xxxxx
}
例如:
public static void show(int... a){
System.out.println(a);
}
*本质
可变参数的本质是一个数组
定义数组: int[] ints ;
可变参数:int... a;
//可变参数中的int 相当于于定义数组时(int[] a)前面的数据类型int 相当于定义数组时数组的名称 (重要)
*注意事项
如果一个方法有多个参数,包含可变参数,可变参数要放在最后
Map集合
*Map集合的特点
键值对映射关系
一个键对应一个值
键不能重复(Set),值可以重复(Collection)
元素存取无序
*Map集合的基本功能
V put(K key,V value) 添加元素 //不是add
V remove(Object key) 根据键删除键值对元素
//根据key删除的是 键值对
void clear() 移除所有的键值对元素
boolean containsKey(Object key) 判断集合是否包含指定的键
boolean containsValue(Object value) 判断集合是否包含指定的值
boolean isEmpty() 判断集合是否为空
int size() 集合的长度,也就是集合中键值对的个数
*Map集合的获取功能
V get(Object key) 根据键获取值
//变相的说明了key和value之间是有对应关系的
Set keySet() 获取所有键的集合
//为什么返回值是一个set集合?
//因为Map集合的key特点是唯一、存取无序,和Set集合的特点一样
Collection values() 获取所有值的集合
//为什么返回值是Collection
//因为value的值是根据key值来的,没有什么特殊的要求
Set<Map.Entry<K,V>> entrySet() 获取所有键值对对象的集合
//entry是条目的意思,这里指的是一条记录 其实就是一个键值对
注意事项:
map集合本身没有迭代器的概念
*Map集合最重要的方法
增:
V put(K key,V value) 添加元素
删:
V remove(Object key) 根据键删除键值对元素
改:
V put(K key,V value) 添加元素 //注意:当key值存在的时候,put方法就是修改, key值不存在put方法就是添加
遍历
1.遍历方式一
遍历的步骤
(1)获取所有的key集合
(2)循环遍历每一个key
(3)根据每一个key去获取对应的value
(4)打印出key---value的对应关系
2.遍历方式二
遍历步骤
(1)获取所有键值对的集合
(2)循环遍历键值对集合,得到每一个键值对对象
(3)获取key和value
key:getKey()
value:getValue()
(4)打印出key---value的对应关系
*什么时候要重写hashCode和equals方法?
(1)自定义对象
(2)自定义对象需要保证唯一的时候
*hashset存储自定义对象
*TreeSet集合存储自定义对象
*LinkedHashSet存储自定义对象
*Map集合的key存储自定义对象
*斗地主案例的实现
File类
*概念
是文件和目录路径名的抽象表示 //目录和文件两个都可以用File类来表示
*注意事项
我给的那个路径,不是一定要真实存在的,所以创建出来的File对象可能是真实存在的,也可能是不存在的
*相对路径和绝对路径的问题
相对路径:一般情况下,不带盘符的就是相对路径
//相对于谁?
//相对于当前的项目(project),而不是模块(module),它和模块是平级的关系
绝对路径:一般情况下,带盘符的就是绝对路径
//"d:\\abc"
递归
*概念
递归指的是"方法"定义中"调用方法本身"的现象 //方法自己调用自己的现象
*递归的两个条件
(1)递归一定要有出口
转化成代码:既然有出口就一定要进行判断是不是出口
if(是否是出口){
}
(2)要有方法自己调用自己的现象 //需要我们找的就是这个规律
show(){
xxxx
show()
}
*总结:
public 返回值 方法名(参数列表 变量名){
if(是出口){
return 返回值;
}else{
return 方法名(); //规律自己找
}
}
*注意事项
(1)递归一定要有出口。否则内存溢出
(2)递归虽然有出口,但是递归的次数也不宜过多。否则内存溢出
*关于递归的总结
不管是自己写递归方法还是看人家的递归代码
*出口的判断
*方法自己调用自己的规律