day01
1.异常
1.1错误和异常
Java中的错误 Error: 是代表JVM本身的错误, 咱们程序员无法通过代码进行处理的
Error很少出现,一旦出现 就意味着大换血了
Java中的异常 Exception: 代表Java程序在运行过程中出现了不可预期的错,然后影响了代码的正常的执行,可以使用Java中的异常的处理机制来出来一下代码,让代码能够正常的执行下去。
1.2异常
Java中封装好了处理异常的机制。
Java异常分为两大类:
编译时异常: 在写代码的编译器会报红
FileNotFoundException
SQLException
ClassNotFoundExcetion
InterruptException
运行时异常: 在运行的时候 出现的异常
ArrayIndexOutOfBoundsException 数组下标越界的异常
当Java代码出现了异常,然后就可以是使用Java的处理机制来处理,如果没有出现异常就正常执行即可
1.3异常的捕捉
在程序运行的过程中,难免会出现异常,这个时候Java中捕捉异常的语法格式将会发现异常并进行异常的处理
try { 可能出现异常的代码 } catch(异常对象) { 语句体(一般是针对上方异常的处理方案) } //执行流程:如果try里面的代码没有出现异常,跳过catch,继续向下运行。 如果try里面代码有异常,就会执行catch大括号里面的语句体
public class Demo1 { public static void main(String[] args) { int[] arr = {5, 3, 9, 10, 8}; try { int a = arr[9];//可能出现异常的代码 } catch (Exception e) {//代码出现异常的原因 System.out.println("下标越位");//出现异常的解决办法 } } }
1.4异常的抛出
在代码出现异常的地方进行抛出异常,特别是关于编译时异常的时候,为了保证代码能够编译通过,可以使用抛出异常的方式进行抛出异常
throws:关键字 名词 告知使用者 此处有异常 一定要注意一下别写错了
public class Demo1 { public static void main(String[] args) throws Exception {//进行了异常的抛出 Thread.sleep(1000);//编译时出现了异常,不抛出无法进行编译 System.out.println("代码能够继续向下运行");//抛出异常后代码继续向下运行 } }
总结:在有异常的地方的方法后面加上:throws 异常类
1.5throw
抛的动作,可以跑出来一个异常的对象,可以自己造错
public class Demo1 { public static void main(String[] args) throws Exception { Scanner scanner = new Scanner(System.in); System.out.println("请输入用户名"); String username = scanner.next(); if (!username.equals("庄庆友")) { throw new Exception("用户名不存在");//造错,如果用户名不对就报错并输出用户名错误,程序终止 } System.out.println("请输入密码"); int user = scanner.nextInt(); if (!(user == 123456)) { throw new Exception("密码错误");//造错,如果密码不对就报错并输出密码错误,程序终止 } System.out.println("登录成功"); } }
1.6自定义异常
开发中会遇到很多的异常,但是Java中给咱们提供的异常不足以描述了。
咱们可以自己造异常
package com.qf.d_exception; //抄别人的异常类 单身人士的异常类 class SinglerException extends Exception{ public SinglerException () { super(); } public SinglerException (String message) { super(message); } } public class Demo7 { public static void main(String[] args) throws Exception { buy(false); } //true 是单身 false 不是单身 public static void buy (boolean isSingle) throws Exception{ if (isSingle) { throw new SinglerException("单身不能进店购买"); } System.out.println("情侣买一送一!!!"); } }
day02
1.String
1.1String的两种声明方式
package com.qf.a_string; public class Demo1 { public static void main(String[] args) { //声明的第一种方式: String string = "我欲乘风归去"; System.out.println(string); //这第二种声明方式 String str1 = new String("goudan"); System.out.println(str1); } }
1.2两个字符串的比较
==:比较的是内存地址
equals:先比较内存地址,地址不一样的话再比较内容
package com.qf.a_string; public class Demo2 { public static void main(String[] args) { String str1 = "abc"; String str2 = "abc"; String str3 = new String("abc"); String str4 = new String("abc"); //== 比较的是内存的地址 System.out.println(str1 == str2);//true System.out.println(str1 == str3);//false System.out.println(str3 == str4);//false // boolean equals(Object anOnject); 比较两个字符串是否相等 //equals 先比较的是内存地址,如果内存地址一样 肯定是一样的。如果地址不一样 再比较内容 //内容一样的话,也true System.out.println(str1.equals(str2));//true System.out.println(str1.equals(str3));//true System.out.println(str3.equals(str4));//true /** * "abc" a "abc"b * a == b 内存不一样 false * 继续比较内容 * a = {'a', 'b', 'c'} b = {'a', 'b', 'c'}; * for () { * if (a[i] == b[i]) { * a[0] == b[0] * a[1] ==b[1] * a[2] == b[2] * } * * } * public boolean equals(Object anObject) { if (this == anObject) {//比较两个内存地址是否一样 return true;//内存地址一样的话就返回true } if (anObject instanceof String) {//true String anotherString = (String)anObject; 强转为字符串 v1 = {'a', 'b', 'c'} v2 = {'a', 'b', 'c'}; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) { v1[0] !=v2[0] v1[1] !=v2[1] v1[2] !=v2[2] return false; } i++; } return true; } } return false; } * */ } }
1.3String类下面的方法
获取字符串长度 int length();
获取指定位置的字符 char charat();
获取指定字符串的下标 int indexOf();
获取指定字符串的最后一个下标 int lastIndexOf();
是否以指定字符串结尾 bollean endsWith();
判断是否为空 bollean isEmpty();
判断是否包含字符串 bollean contains();
判断两个字符串是否相等 bollean equals();
忽略大小写判断是否相等 bollean equalslgnoreCase();
将字符数组变为字符串:
String();
String();
String.valureOf();
将字符串转化为字符数组:toCharArray();
将字符替换进字符串:String replace();
以regex对字符串进行切割:String split(String regex);
截取字符串的一部分:String subString();
String subString();
将小写字母转化为大写:String toUpperCase();
将大写字母转为小写:String toLowerCase();
去除左右空格:String trim();
2.泛型
2.1为什么要用泛型
广泛的类型
在开发中对数据一致性的要求是比较高的
例如:
数组中存数据 都是同一个类I型的
int[] arr = new int[]
char[] arr = new char[]
import java.util.ArrayList; import java.util.Scanner; public class Demo1 { public static void main(String[] args) { int[] arr = {1,2,3,4}; //STring类不用导包 为啥? 因为 在 java.lang包下面 String[] strs = {"nx", "hsjj", "xnjasjn"}; //数组确实可以保证数据类型一致性,但是开发不用!!!为啥? 数组的方法比较少,而且数组容量提前定好的 //不太方便 咋办?学习集合 明天讲 //集合的作用是和数组类似 都是容器 用来存数据的 ArrayList list = new ArrayList();//list 就是容器 就是用来存数据的 list.add("狗蛋");//添加 list.add(34); list.add('智'); list.add(true); System.out.println(list); //以上代码好不好?不好!!!在开发中对数据一致性的要求是比较高的 //获取一个数据 String obj = (String)list.get(0); System.out.println(obj); //需要强转不好,现在有一个技术 泛型 可以保证咱们的数据一致性 针对于集合 //ArrayList<类> //<String> 告知编译器 此集合只能存放String类型的数据 一致性保证一下 ArrayList<String> list1 = new ArrayList<String>(); list1.add("张三"); list1.add("王二"); list1.add("wangliu"); System.out.println(list1); String str1 = list1.get(0); System.out.println(str1); //泛型在<类> 如果放int类型的数据===》Integer ArrayList<Integer> list2 = new ArrayList<Integer>(); list2.add(23); //扩展: 八大基本数据类型所对应的包装类 /** * byte ==> Byte * short===>Short * int===>Integer * long===>Long * boolean-===>Boolean * char===>Character * float===>Float * double====>Double */ } }
2.2自定义泛型在方法中的使用
语法格式:
public <无意义的占位符> 返回值的类型 方法的名字(参数) { }
无意义的占位符:可以是任何大写字母,但是一般是T(Type)E(element)
package com.qf.b_fanxing; public class Demo3 { public static void main(String[] args) { test(); } //无参无返回值的方法 没有必要使用泛型 public static <T> void test () { System.out.println("hewllo world"); } //有参无返回值的方法 可以使用 public static <T> void test (T t, T t1) { //t 是多个类型 操作的时候要符合数据类型相对应的操作方式 System.out.println(t + "" + t1); } //无参 有返回值的 没有必要使用泛型的 public static <T> T test1 () { return null; } //有参 有返回值的方fa 写起来有局限性 public static <T> T test2 (T t, T t1) { return t; } //有参的才有一点用途 }
2.3泛型类
语法格式:
class 类名<无意义的占位符> { }
package com.qf.b_fanxing; class Person<T> { //泛型类中可以有普通的方法 public void eat() { System.out.println("吃饭饭"); } //带有泛型的方法 //public <T> void test() {一般在泛型类中的方法中不要去写<T> 因为方法的参数 根据类后面的<T> 来约束的 public void test (T t) { System.out.println(t); } //静态方法 自娱自乐 //当创建对象的时候才确定泛型类型,静态方法早于对象的创建,所以你的泛型对我是没有任何用的 //告知程序员此处的泛型和类的泛型没有关系的 public static <E> void test1 (E e) { System.out.println(e); } } public class Demo4 { public static void main(String[] args) { Person.test1(78); Person<String> strPerson = new Person<String>(); //现在T是String 就意味者在调用test方法的时候 参数只能传String strPerson.test("goudan"); //strPerson.test(89); } }
2.4泛型抽象类
语法格式:
abstract class 类名<无意义的占位符> { }
package com.qf.b_fanxing; abstract class A<T> { abstract void test(T t); } //1.在继承抽象类时候 子类也必须带有和父类相同的泛型占位符 class ATest<T> extends A<T> { void test(T t) { } } public class Demo5 { public static void main(String[] args) { ATest<Integer> aTest = new ATest(); aTest.test(67); } }
2.5泛型接口
语法格式:
interface 接口名字<无意义的占位符> { }
package com.qf.b_fanxing; interface B<T> { void test(T t); } class BTest<T> implements B<T> { public void test(T t) { System.out.println("xixi "); } } public class Demo6 { public static void main(String[] args) { } }
day03
1.权限修饰符
权限: 针对于类,属性, 方法 在使用他们的时候是有权限的。
其实已经见过权限修饰符:
public private 默认的 (啥也不写) protected
修饰类 , 属性, 方法 等
1.1private
private 私有的
可以用来修饰属性和方法
1.私有化的所有的只能在本类中使用 其他地方用不了
1.2默认的
修饰方法和属性
1.同一个类中 可以访问的
2.在同一个包下面的 其他类可以使用
3.在不同包下面 的子类和其他类都不能使用
1.3protected
受保护的
修饰属性和方法
1.同一类中可以使用
2.同一个包下面 的不同类 可以使用
3.不同包下面的子类可以使用的
4.不同包下面的其他类不可以使用
1.4public
公开的
在本类中, 同一个包下面 不同类。 不同包下面所有的类 都可以使用的
总结:
权限修饰符 | 当前类 | 当前包的其他类 | 其他包的子类 | 其他包的其他类 |
---|---|---|---|---|
private | 可以 | 不可以 | 不可以 | 不可以 |
默认的 | 可以 | 可以 | 不可以 | 不可以 |
protected | 可以 | 可以 | 可以 | 不可以 |
public | 可以 | 可以 | 可以 | 可以 |
2.集合
2.1为什么要有集合
集合和数组都是可以存数据的
真是开发的时候用的是集合而不是数组
1.数组的容量是固定的
2.数组封装的方法比较少
Java有封装的集合类库,只需要实例化出来一个对象,适用对象调用方法就可以完成数据相关的操作
2.2集合结构
interface Collection<E>java中集合的总接口
List<E> Collection的子接口,特征:存放数据是有序的,可以重复的
ArrayList<E>List的实现类,里面写好了List接口所有的抽象方法,功能也写好了,底层是数组
LinkedList<E>List的实现类,里面有自己独有的方法,底层是链表
Set<E> Collection的子接口,特征:存放数据是无序的不可重复的
HashSet<E>Set的实现类,底层是Hash算法
TreeSet<E>Set的实现类,底层是二叉树
2.3Collection接口
Collection下面的方法: 增: 添加数据:add(); 将一个集合添加到另一个集合中:addAll(); 删: 删除指定的元素:remove(); 删除集合中所有的另一个集合的元素:removeAll(); 清空集合:clear(); 查: 集合中元素的个数:size(); 将集合转化为数组:toArray(); 在集合中是否包含:contions(); 判断一个集合是否是另一个集合的子集:contionsAll(); 判断是否为空:isEmpty();
package com.qf.c_collection; import java.util.ArrayList; import java.util.Collection; public class Demo1 { public static void main(String[] args) { //父类的引用指向了子类的对象,意味着 对象只能调用父类的方法 //对象不能调用子类独有的方法 只能调用重写的方法 //Collection 所有的抽象方法都被ArrayList 重写了 Collection<String> collection = new ArrayList<String>();//容器 只能放String类型的数据 collection.add("红旗渠"); collection.add("散花"); collection.add("黄鹤楼"); System.out.println(collection);//[红旗渠, 散花, 黄鹤楼] Collection<String> collection1 = new ArrayList<String>();//容器 只能放String类型的数据 collection1.add("舍得"); collection1.add("茅台"); collection1.add("牛栏山"); System.out.println(collection1); collection.addAll(collection1);//将collection1这个集合添加到collection中 System.out.println(collection);//[红旗渠, 散花, 黄鹤楼, 舍得, 茅台, 牛栏山] // Collection<Integer> collection2 = new ArrayList<Integer>();//容器 只能放String类型的数据 // collection2.add(1); // collection2.add(2); // collection2.add(3); // // collection.addAll(collection2); System.out.println(collection.remove("舍得"));//删除指定的数据 System.out.println(collection);//删除之后的结果 //[红旗渠, 散花, 黄鹤楼, 茅台, 牛栏山] //System.out.println(collection.remove("舍得")); Collection<String> collection3 = new ArrayList<String>();//容器 只能放String类型的数据 collection3.add("舍得"); collection3.add("茅台"); collection3.add("牛栏山"); collection3.add("红花郎"); System.out.println(collection3);//[舍得, 茅台, 牛栏山, 红花郎] collection.removeAll(collection3);//删除指定集合中包含的所有此集合的元素 //在collection 删除collection3 中包含的元素 System.out.println(collection);//[红旗渠, 散花, 黄鹤楼] collection.clear(); System.out.println(collection);//[] } }
2.4遍历集合中的数据
3种 1.使用for循环 2.使用增强for循环【重点】 3.使用迭代器【重点】
2.4.1使用for循环
package com.qf.c_collection; import java.util.ArrayList; import java.util.Collection; public class Demo2 { public static void main(String[] args) { Collection<Character> list = new ArrayList<Character>(); list.add('a'); list.add('b'); list.add('c'); System.out.println(list);//[a, b, c] //for循环遍历 //将集合转为数组 然后再遍历 Object[] objs = list.toArray(); for (int i = 0; i < objs.length; i++) { System.out.println(objs[i]); } } }
2.4.2使用增强for
语法格式:
for (集合或数组的数据类型 临时变量名字 : 集合或者数组) { } //将集合中的元素依次赋值给临时变量,输出的时候直接输出临时变量就可以
public class Demo1 { public static void main(String[] args) throws Exception { ArrayList<String> arrayList = new ArrayList<>(); arrayList.add("dafsd"); arrayList.add("fadsfas"); arrayList.add("fadsfasdaas"); arrayList.add("fadsfadsdas"); for (String arr : arrayList) {//增强for循环 System.out.println(arr); } } }
2.4.3迭代器
package com.qf.c_collection; import java.util.ArrayList; import java.util.Collection; public class Demo3 { public static void main(String[] args) { Collection<String> liStrings = new ArrayList<String>(); liStrings.add("张三"); liStrings.add("狗蛋"); liStrings.add("老邢"); System.out.println(liStrings); //使用增强for循环遍历数据 for (String s : liStrings) { System.out.println(s); } } }
2.5List接口
Collection是List接口的父接口
为什么开发用List而不用Collection
List接口下面有自己独有的方法,子接口的功能比父接口的功能全
子接口不但重写了父类接口所有的方法,而且加了自己独有的方法
所以开发用List
List接口储存数据的时候 有序的 可以重复的 增: 在指定索引位置的下标插入一个数据:add(); 在指定下标的位置插入另一个集合:addAll(); 删: 通过索引删除数据,返回的是被删除的元素:remove(); 改: 指定下标的元素被别的元素替代,返回的是被替代的元素:set(); 查: size(); toArry(); contians(); isEmpty(); 获取指定下标的元素:get(); 获取指定元素的下标:indexOf(); 获取指定元素最后一次出现的位置:lastindexOf(); 截取集合中的一段:subList();
import java.util.ArrayList; import java.util.List; public class Demo1 { public static void main(String[] args) { List<String> list = new ArrayList<String>(); System.out.println(list); list.add("狗蛋"); list.add("张三"); list.add("李四"); list.add("张三"); list.add("李四"); list.add("李四"); list.add("张三"); System.out.println(list);//[狗蛋, 张三, 李四] list.add(1, "王五");//在指定的下标的位置上面添加一个元素 System.out.println(list);//[狗蛋, 王五, 张三, 李四] //通过元素删除 System.out.println(list.remove("二蛋"));//false //通过索引下标删除 System.out.println(list.remove(0));//狗蛋 // System.out.println(list);//被删除之后的集合元素 //[王五, 张三, 李四] System.out.println(list.set(1, "王八"));//返回值是被替换的元素 System.out.println(list);//[王五, 王八, 李四] System.out.println(list.get(2));//李四 System.out.println(list.indexOf("王博"));//-1 System.out.println(list.indexOf("王八"));//1 System.out.println(list.lastIndexOf("张三"));//6 //list = [王五, 王八, 李四, 张三, 李四, 李四, 张三] System.out.println(list.subList(2, 4));//[李四, 张三] } }
2.6接口List数据遍历
for循环
增强for循环
迭代器
List集合不用转为数组就能使用for循环进行遍历
import java.util.ArrayList; import java.util.List; public class Demo2 { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("狗蛋"); list.add("张三"); list.add("李四"); list.add("张三"); list.add("李四"); list.add("李四"); list.add("张三"); System.out.println(list);//[狗蛋, 张三, 李四] for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } } }
2.6.1迭代器
import java.util.ArrayList; import java.util.List; import java.util.ListIterator; public class Demo4 { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("狗蛋"); list.add("张三"); list.add("李四"); list.add("张三"); list.add("李四"); list.add("李四"); list.add("张三"); // ListIterator<String> listIterator = list.listIterator(); // while (listIterator.hasNext()) { // System.out.println(listIterator.next()); // } // //循环结束代码走到这一步 咱们的光标在 最后一个位置 // System.out.println("========="); // //上一个是否有元素 // while (listIterator.hasPrevious()) { // //返回上一个元素并且 光标向上一个 // System.out.println(listIterator.previous()); // } ListIterator<String> listIterator = list.listIterator(list.size()); while (listIterator.hasPrevious()) { System.out.println(listIterator.previous()); } } }
List集合的迭代器
可以从前往后遍历,也可以从后往前遍历
day04
1.List集合中存自定义对象
集合容器中存数据的时候,可以存字符串整型等,也可以存储对象,集合中带有泛型,是一个类
存储对象的时候,为了方便获取对象数据,所以要重写toString方法
package com.qf.b_list; import java.util.ArrayList; import java.util.List; class Person { private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override//重写的toString方法 Object类 //将内存地址 转为一个字符串表示的形式 可以看到打印的数据 不再是一个内存地址了 //而是一个字符串了 public String toString() { return "[name:" + name + ", age:" + age + "]"; } } public class Demo1 { public static void main(String[] args) { List<Person> persons = new ArrayList<Person>(); Person person1 = new Person("狗蛋", 24); Person person2 = new Person("张三", 14); Person person3 = new Person("王五", 34); persons.add(person1); persons.add(person2); persons.add(person3); System.out.println(persons); //以上在存数据 //可以取数据 // System.out.println(persons.get(0));//获取的是集合中第一个对象 // System.out.println(persons.get(1)); // System.out.println(persons.get(2)); // // //想只把狗蛋取出来 // //先取出来第一个person对象 // System.out.println(persons.get(0).getName()); for (Person person : persons) { System.out.println(person.getName() + ":" + person.getAge()); } } }
day05
1.Object类
Object类是所有类的基类,父类
public String toString()
返回对象的字符串表现形式,一般来说toString返回一个“textually“这个对象的字符串,结果应该是一个简明扼要的表达,容易让人阅读,建议所有子类重写此方法
package com.qf.a_object; class Person { String name; int age; @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } } public class Demo1 { public static void main(String[] args) { Object object = new Object(); Object object2 = new Object(); System.out.println(object); // 该toString类方法Object返回一个由其中的对象是一个实例, //该符号字符`的类的名称的字符串@ ”和对象的哈希码的无符号的十六进制表示。 换句话说,这个方法返回一个等于下列值的字符串: // // getClass().getName() + '@' + Integer.toHexString(hashCode()) System.out.println(object.toString());//java.lang.Object@15db9742 Person person = new Person(); System.out.println(person); } }
public bollean equals(Object obj)
指示一些其他对象与此对象是否相等,判断两个对象是否相等
public boolean equals(Object obj) { return (this == obj); }
==:比较的是内存地址
object1.equals(object2)
为啥String类下面equals如果内容一样的可以返回true?当父类的需求满足不了子类的需求
String类重写了父类的equals方法
class Student { String name; int age; public Student(String name, int age) { this.name = name; this.age = age; } @Override public boolean equals(Object obj) { if (this == obj) {//如果内存地址一样 就返回true return true; } //如果内存地址不一样,但是内容一样咋写 //equals(Object obj) Object obj = student2;//向上转型 //student1.equals(student2) //obj instanceof Student if (obj instanceof Student) {// student2 instanceof Student true Student stu = (Student)obj;//向下转型 //student2===>stu // student1.name.equals(student2.name) true && student1.age == student2.age return this.name.equals(stu.name) && this.age == stu.age; } return false; } @Override public String toString() { return "Student [name=" + name + ", age=" + age + "]"; } } public class Demo3 { public static void main(String[] args) { Student student1 = new Student("土豆", 12); Student student2 = new Student("土豆", 12); System.out.println(student1.equals(student2));//因为Object类是Student的父类 //以上是false, 但是真实的开发的时候,关注的是内容 而不是地址 //只要内容一样也得给我true。 咋办?重写eqauls方法 } }
public int hashCode()
返回的哈希码值,支持这种方法是为了散列表,如HashMap提供的那样 。
在Object类中,将十六进制的内存地址转为十进制的值,就叫hash值
如果内存地址不一样,那么哈希也是不一样的
hashCode
的总合同是:如果根据
equals(Object)
方法两个对象相等,则在两个对象中的每个对象上调用hashCode
方法必须产生相同的整数结果
class Man { int id; String name; public Man(int id, String name) { super(); this.id = id; this.name = name; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof Man) { Man man1 = (Man)obj; return man1.id == this.id && man1.name.equals(this.name); } return false; } @Override public int hashCode() { return name.hashCode(); } } public class Demo6 { public static void main(String[] args) { //比较两个对象的而内容是否一致 Man man1 = new Man(2, "蔡徐坤"); Man man2 = new Man(2, "蔡徐坤"); System.out.println(man1.equals(man2));//true //如果根据`equals(Object)`方法两个对象相等, //则在两个对象中的每个对象上调用`hashCode`方法必须产生相同的整数结果 System.out.println(man1.hashCode()); System.out.println(man2.hashCode()); //只能重写hashCode方法 不要再使用Object类下面hashCode } }
由此可得出结论,重写equals方法时,通常也要重写hashCode方法,一边维护hashCode方法的通用合同,该方法规定相等的对象必须具有相等的哈希码值
class Man { int id; String name; public Man(int id, String name) { super(); this.id = id; this.name = name; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + id; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Man other = (Man) obj; if (id != other.id) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } } public class Demo6 { public static void main(String[] args) { //比较两个对象的而内容是否一致 Man man1 = new Man(2, "蔡徐坤"); Man man2 = new Man(2, "蔡徐坤"); System.out.println(man1.equals(man2));//true //如果根据`equals(Object)`方法两个对象相等, //则在两个对象中的每个对象上调用`hashCode`方法必须产生相同的整数结果 System.out.println(man1.hashCode()); System.out.println(man2.hashCode()); //只能重写hashCode方法 不要再使用Object类下面hashCode } }
2.Set集合
Set集合也是用来存数据的
Set集合父接口为Collection接口
Set存数据的时候是无序的不可重复的
Set接口下面有两个实现类:
HashSet:底层是hash值进行储存的,如果hash纸一样的就无法存到集合中
TreeSet:底层是二叉树,对存入的数据进行自然排序
package com.qf.b_hashSet; import java.util.HashSet; import java.util.Set; class Person { int id; String name; public Person(int id, String name) { this.id = id; this.name = name; } @Override public String toString() { return "Person [id=" + id + ", name=" + name + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + id; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (id != other.id) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } } public class Demo2 { public static void main(String[] args) { Set<Person> set = new HashSet<Person>(); Person person1 = new Person(1, "老邢"); Person person2 = new Person(2, "骚磊"); Person person3 = new Person(1, "老邢"); set.add(person1); set.add(person2); set.add(person3); System.out.println(set); //现在存了三个对象。感觉可以吗?不可以 关注是内容!!!咋干掉? //重写equlas和hashCode方法 } }
总结: 如果将对象存入到hashSet中的时候,必须重写当前类的equals和hashCode方法 为了保证对象的内容不重复。
HashSet的底层是HashMap JDK1.8 数组 + 链表 + 红黑树 三个组成
2.1TreeSet
也是Set的实现类,可以保证数据唯一性,储存也是无序的,同时会对存入的数据进行自然排序
2.2TreeSet存自定义的对象
存对象的时候需要在类的实现Comparable这个接口。该接口对实现它的每个类的对象强加一个整体排序。 这个排序被称为类的自然排序 ,类的compareTo
方法被称为其自然比较方法 。
int compareTo(T o)
将此对象与指定的对象进行比较以进行排序。
返回一个负整数,零或正整数,因为该对象小于,等于或大于指定对象。
person1.compareTo(person2)
如果返回值是负数, 就证明 person1小于 person2 排序 person1 排在person2前面
如果返回值是0,就证明相等,不存了
如果是个正数,就证明大于 person2 排在person1的前面
package com.qf.c_treeset; import java.util.Set; import java.util.TreeSet; class Person implements Comparable<Person>{ String name; int age; public Person(String name, int age) { super(); this.name = name; this.age = age; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } @Override public int compareTo(Person o) { // TODO Auto-generated method stub //1. this 就是狗蛋 o也是狗蛋 //2. this 潘周丹 o狗蛋 【 潘周丹 狗蛋】 //3. this 旺财 o狗蛋 【潘周丹 狗蛋 旺财 】 //4.this erdsan o狗蛋 【潘周丹 狗蛋 旺财 】 int num = this.age - o.age; return num; //2.[潘周丹 狗蛋] //3.[旺财 狗蛋] //4.[潘周丹 旺财erdan 狗蛋] } } public class Demo2 { public static void main(String[] args) { Set<Person> set = new TreeSet<Person>(); set.add(new Person("狗蛋", 24));//这行开始报错 set.add(new Person("潘周丹", 21)); set.add(new Person("旺财", 23)); set.add(new Person("erdsan", 24)); //Exception in thread "main" java.lang.ClassCastException: //com.qf.c_treeset.Person cannot be cast to java.lang.Comparable System.out.println(set); //按照age进行排序 } } //按照年龄进行排序
package com.qf.c_treeset; import java.util.Set; import java.util.TreeSet; //先按照年龄 如果年龄 再字符串比较 class Student implements Comparable<Student>{ String name; int age; public Student(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Student [name=" + name + ", age=" + age + "]"; } @Override public int compareTo(Student o) { int num = this.age - o.age; if (num == 0) {//当 age 相等的时候 int num1 = this.name.compareTo(o.name); return num1; } return num; } } public class Demo3 { public static void main(String[] args) { Set<Student> set = new TreeSet<Student>(); set.add(new Student("b", 65)); set.add(new Student("d", 35)); set.add(new Student("c", 25)); set.add(new Student("a", 35)); set.add(new Student("浩正", 35)); System.out.println(set); } }
通过查阅API我们得知TreeSet集合是基于TreeMap的实现,而TreeMap是基于二叉树(红黑树)结构,也就是说TreeSet集合的底层使用的二叉树(红黑树)结构。
树结构:它也是数据结构中的一种。在计算机领域中树结构指的是倒立的树。
树结构存储的数据,每个数据也需要节点来保存。
而TreeSet集合底层是二叉树的数据结构,什么是二叉树呢?
二叉树:每个节点的下面最多只能有2个子节点。
说明:最多表示一个节点下面可以有两个子节点或者一个子节点或者没有子节点。
在二叉树的根节点左侧的节点称为左子树,在根节点的右侧的节点称为右子树。
既然已经得知TreeSet集合底层是二叉树,那么二叉树是怎样存储数据的呢?是怎样保证存储的数据唯一并有序的呢?
二叉树的存储流程:
当存储一个元素的时候,如果是树的第一个元素,这个元素就作为根节点。
如果不是第一个元素,那么就拿要存储的元素与根节点进行比较大小:
大于根元素:就将要存储的元素放到根节点的右侧,作为右叶子节点。
等于根元素:丢弃。
小于根元素:就将要存储的元素放到根节点的左侧,作为左叶子节点。
总结:二叉树是通过比较大小来保证元素唯一和排序的。 20 10 31 5 13 23 51
关于TreeSet在存储数据的时候,会有一个排序的问题u,。使用一个接口叫Comparable这个接口
咱们还可以使用另外一种方式进行排序。叫比较器的写法
TreeSet(Comparator<? super E> comparator)
构造一个新的,空的树集,根据指定的比较器进行排序。
package com.qf.c_treeset; import java.util.Comparator; import java.util.TreeSet; class Emp { String name; int age; public Emp(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Emp [name=" + name + ", age=" + age + "]"; } } class MyComparator implements Comparator<Emp> { @Override public int compare(Emp o1, Emp o2) { // TODO Auto-generated method stub int num1 = o2.age - o1.age; return num1; } } public class Demo4 { public static void main(String[] args) { //借助于有参的构造方法 //TreeSet(Comparator<? super E> comparator) //构造一个新的,空的树集,根据指定的比较器进行排序。 TreeSet<Emp> emps = new TreeSet<Emp>(new MyComparator()); emps.add(new Emp("狗蛋", 23)); emps.add(new Emp("狗蛋1", 22)); emps.add(new Emp("狗蛋2", 24)); emps.add(new Emp("狗蛋3", 21)); System.out.println(emps); } }
3.匿名内部类
为了减少代码量
3.1基于抽象类匿名内部类
package com.qf.d_niming; abstract class Person { public abstract void eat(); } //class Man extends Person { // // @Override // public void eat() { // // TODO Auto-generated method stub // // } // //} public class Demo1 { public static void main(String[] args) { // Person person = new Person() { // @Override // public void eat() { // // TODO Auto-generated method stub // System.out.println("吃饭"); // } // }; // person.eat(); new Person() { @Override public void eat() { // TODO Auto-generated method stub System.out.println("吃汉堡"); } }.eat(); } }
不用再创建类去继承抽象类就可以直接重写抽象类的方法并调用
真实开发的用法:一个方法的参数是一个抽象类
package com.qf.d_niming; abstract class Student { public abstract void sleep(); } public class Demo2 { public static void main(String[] args) { //test方法参数是抽象类的对象 test(new Student() { @Override public void sleep() { // TODO Auto-generated method stub System.out.println("睡得老想了"); } }); } public static void test (Student stu) { stu.sleep(); } }
3.2基于接口的匿名内部类
package com.qf.d_niming; interface Dog { void lookHome(); } public class Demo3 { public static void main(String[] args) { new Dog() { @Override public void lookHome() { // TODO Auto-generated method stub System.out.println("看家比较负责任"); } }.lookHome(); } }