1、API体系【二大家族:Collection、Map家族】
1.1、Collection【表】
/**
* 集合工具类Collections常用的API演示
*/
public class Demo6 {
public static void main(String[] args) {
Book book1 = new Book(1 , "java编程思想" , 97.5);
Book book2 = new Book(2 , "mysql从入门到精通" , 56.4);
Book book3 = new Book(3 , "java设计模式" , 109.2);
List<Book> list = new ArrayList<>();
list.add(book1);
list.add(book2);
list.add(book3);
// 对集合进行自定义排序!! 调用sort方法,传参两个参数:1、集合 ; 2、Comparator对象
Collections.sort(list, (o1, o2) -> (int)(o2.bookPrice - o1.bookPrice));
System.out.println(list);
// 反转
Collections.reverse(list);
System.out.println(list);
// 从集合中查找最大的哪个对象 -> 表达的意思(找价格最高的哪个图书对象!!)
Book maxBook = Collections.max(list , (o1, o2) -> (int)(o1.bookPrice - o2.bookPrice));
System.out.println("价格最高的图书是:" + maxBook);
// 查找图书价格最低的图书对象
Book minBook = Collections.min(list , (o1, o2) -> (int)(o1.bookPrice - o2.bookPrice));
System.out.println("价格最低的图书是:" + minBook);
// 集合元素的拷贝 (不推荐使用)
List<Book> newBooks = new ArrayList<>();
Collections.copy(newBooks , list);
System.out.println(newBooks);
// 查找学生对象中年龄最大的,最小的。
}
}
1.1.1、sort对List进行排序,但是要创建一个Comparator对象(外部排序,位于java.until包下)
1.1.2、reverse对集合元素进行反转
1.1.3、max查询集合元素中最大的元素,要配合Comparator一起使用
1.1.4、min查询集合元素中最小的元素,要配合Comparator一起使用
1.1.5、相关案列【使用集合保存3个学生对象】
需求:
1)Student类 :学号id, 姓名name,年龄age,生日birthday
2)创建3个Student对象,放到集合中。
3)遍历输出 : 输入学生所有的信息
public class Test {
public static void main(String[] args) throws ParseException {
// 新的技术,把日期格式的字符转换为Date类型
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); // 日期转换器对象
Date d = format.parse("1992-10-11"); // 字符串转换Date类型
// 第一个学生:1 陈永仁 30 1992-10-11
Student stu1 = new Student();
stu1.setId(1);
stu1.setName("陈永仁");
stu1.setAge(30);
stu1.setBirthday(d);
// 第二个学生:2 刘建明 29 1993-10-11
Student stu2 = new Student(2 , "刘建明" , 29 , format.parse("1993-10-11"));
// 第三个学生:3 小明 20 2002-10-11
Student stu3 = new Student(3 , "小明明" , 20 , format.parse("2002-10-11"));
// 把三个学生对象撞到集里
List<Student> list = new ArrayList<>();
list.add(stu1);
list.add(stu2);
list.add(stu3);
System.out.println("===========学生列表==============");
list.forEach((Student e) -> {
// 1 陈永仁 30 Sun Oct 11 00:00:00 CST 1992
// TODO 把日期Date转换为字符串
System.out.println(e.getId() + "\t" + e.getName() + "\t" + e.getAge() + "\t" + e.getBirthday());
});
}
}
1、根据学号查询学生
需求:定义一个静态方法
//就是你要查询的学生对象 // 把你的集合对象, 学号
public static Student findStudentById(ArrayList coll, int id) {
......
}
2、根据姓氏查找学生
需求:定义一个静态方法
public static Collection<Student> findStudentBySurName(Collection<Student> coll , String n) {
......
}
3、根据名查找学生
需求:定义一个静态方法,应该用subString(1),或 endsWith
public static Collection findStudentByName(Collection coll , String n) {
......
}
4、根据学号修改学生的姓名
需求:定义一个静态方法(id 是学号, n是修改后的姓名)
public static void updateStudentNameById(List<Student> coll , int id , String n) {
......
}
5、根据学号删除学生
需求:定义一个静态方法
public static void deleteStudentById(List<Student> coll ,int id) {
......
}
6、保存3本图书,价格进行排序
需求:TreeSet集合来完成
图书类:Book, 属性有:图书编号id , 图书名称bookName , 图书价格bookPrice。重写toString方法。
/**
* 图书类
*/
public class Book implements Comparable<Book> {
public int id;
public String bookName;
public double bookPrice;
@Override
public int compareTo(Book o) {
// 降序
return (int)(o.bookPrice - this.bookPrice);
}
@Override
public String toString() {
return id + "\t " + bookName + "\t bookPrice \t" + bookPrice;
}
}
import java.util.Scanner;
import java.util.TreeSet;
public class Demo4 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
TreeSet<Book> treeSet = new TreeSet<>();
for (int i = 1; i <= 3; i++) { // 循环3次添加3本本
// 每循环一次就造一个图书对象
Book book = new Book();
System.out.println("请输入第"+i+"本图书的信息:");
System.out.print("编号:");
book.id = input.nextInt();
System.out.print("名称:");
book.bookName = input.next();
System.out.print("价格:");
book.bookPrice = input.nextDouble();
// 添加到集合中
treeSet.add(book);
}
System.out.println("编号\t\t名称\t\t价格");
for (Book book : treeSet) {
System.out.println(book);
}
}
}
1.2、Map【字典】
1.2.1、双列,存储键值对这种数据
1.2.2、HashMap
1、常用方法
put:添加元素,key不能重复
containsKey:集合中查询某个key是否存在
remove:根据key删除元素
get:根据key获取相应的value
values:对应内存圈,所有的value值,是一个Collection集合
entrySet:对应内存圈、代表一行,一行就是一个Entry对象
keySet:对应内存圈,左边所有的key值,是一个set集合
2、相关案例,一对一的关系
public class Mop {
/*
集合第二大家族:Mop(字典)
*/
public static void main(String[] args) {
//map内存结构
HashMap<String ,String > map = new HashMap<>();
map.put("001","貂蝉");//key不可重复,values可重复
map.put("002","吕布");
map.put("003","张飞");
map.put("004","关羽");
System.out.println(map);
boolean r = map.containsKey("005");//判断005是否存在
System.out.println(r);
//拿到左边的key这一列
Set<String> keys = map.keySet();
//Map遍历方式1
for (String key:keys) {
Object val = map.get(key);//通过key来找对应的value
System.out.println(key + "->" + val);
}
//Map遍历方式2【拿到右边的value这一列】
Collection<String> values = map.values();
for (String value:values) {
System.out.println(value);//不能通过value找到对应的key
}
//遍历每一行
Set<Map.Entry<String,String>> entries = map.entrySet();
for (Map.Entry<String,String> row:entries) {
String key = row.getKey();
String value = row.getValue();
System.out.println(key + "->" + value);
}
}
}
3、保存电脑城的电子清单【一对多的关系:Map<String,List>map=new HashMap()】
1.3、Comparable和Comparator
1.3.1、区别
1)包的位置不同
Comparable(自然排序)是在集合内部定义的方法实现的排序,位于java.lang下;
Comparator(外部排序)是在集合外部实现的排序,位于java.util下;
2)定义不同
Comparable是排序接口,若一个类实现了Comparable接口,就意味着“该类支持排序”。而 Comparator是比较器,我们若需要控制某个类的次序,可以建立一个“该类的比较器”来进行排序。
Comparable相当于“内部比较器”,而Comparator相当于“外部比较器。
1.4、set集合
1.4.1、HashSet能存储不重复的对象
1.4.2、TreeSet要求自定义对象必须实现Comparable接口
1.4.3、遍历set集合也是通过foreach
1.5、排序
1.5.1、自然排序Comparable【compataTo(o)】
1)升序:this.xx - o.xx
2)降序:o.xx - this.xx
1.5.2、外部排序Comparator【compara(o1,o2)】
1)升序:o1.xx -o2.xx
2)降序:o2.xx - o1.xx
1.6、API差异
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-brbFPL9e-1664470090387)(E:\笔记\第四周\QQ截图20220913141703.png)]
1.7、构建相对文件的系统
1.7.1、try【尝试、可能】
1.7.2、catch【捕获】catch关闭JVM:System.exit()
1.7.3、finall【最终的】无论什么情况都要执行,除非catch语句里关闭JVM则不执行
1.7.4、throw【抛出】
throw new Exception("对不起,你输入错误!");
1.7.5、throws【声明】
public class Demo6 {
static Logger logger = Logger.getLogger(Demo6.class);
// throws 关键字是声明异常的类型。目的是告诉调用者产品的异常类型是什么。。
public static void t() throws Exception {
// 控制 , 数组下标越界, 算术 , 输入类型不匹配
Scanner input = new Scanner(System.in);
System.out.println("请输入课程代号:1~3");
int num = input.nextInt();
if (num == 1) {
System.out.println("c#课程");
} else if(num ==2) {
System.out.println("java课程");
} else if(num == 3) {
System.out.println("html课程");
} else {
// 还不够严格。 我们想严格一点,我们想和JVM一样严格(JVM发送程序有问题直接产生一个异常对象)
// 我们可不可以也产生一个异常对象呢,并抛出去
// System.out.println("对不起,你输入错误");
// 异常抛出去了,就一定要有捕获
throw new Exception("对不起,你输入错误!"); // 走到这一行代码,会发生什么情况。。
// System.out.println("---"); // throw挨着的下一行不能在写其它代码了,否则编译报错!
}
}
public static void main(String[] args) throws Exception {
try {
t();
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
}
}
1.7.6、异常的分类
//异常
int a = 10;
int b = 0;
//JVM会开一个ExceptionThread线程监听
//程序如果出现问题,线程它首先会记录信息,然后
//1、出现问题的位置
//2、出现问题的原因(异常类型)
//3、封装成异常对象,发送给程序员
int c = a / b;//发生异常,因为除数不能为0
System.out.println(c);//因此此代码没有被执行
1.7.7、常见的异常
InputMismatchException:输入类型不匹配异常
打印异常信息:ex.printStackTrace()方法
1.7.8、try-catch捕获异常
public class Demo4 {
public static void main(String[] args) {
int a = 10;
int b = 0;
//JVM会开一个ExceptionThread线程监听
//程序如果出现问题,线程它首先会记录信息,然后
//1、出现问题的位置
//2、出现问题的原因(异常类型)
//3、发送给程序员
//int c = a / b;//发生异常,因为除数不能为0
//System.out.println(c);//因此此代码没有被执行
//数组下标越界异常
int[] nums = new int[2];
// int[] sum = null;
//会出现问题的代码
try{
int d = nums[3];
// int e = sum[2];
}
// catch (ArrayIndexOutOfBoundsException ex){//ArrayIndexOutOfBoundsException ex数组下标异常
// System.out.println("程序下标越界了");
// ex.printStackTrace();//打印程序异常的错误信息
// }catch (NullPointerException ex){//NullPointerException ex空指针异常
// System.out.println("空指针异常");
// }
catch (Exception ex){//Exception ex:所有异常都应该走这条分支
System.out.println("程序错误了");
}finally {//不管是否出现异常,都要执行
System.out.println("程序死活都有执行");
}
System.out.println("程序正常终止");
}
}
1.7.9、异常匹配机制
1.7.10、创建日志对象
import org.apache.log4j.Logger;
public class Demo5 {
// 是受保护的,只有子类才能new
// 创建一个日志记录对象
static Logger logger = Logger.getLogger(Demo5.class);
public static void main(String[] args) {
// 键盘输入的地方,添加一个try-catch语句,防止出现异常
int[] nums = null;
// 寻找可能会出现问题的代码
try { // try包含的代码有可能会出问题
int c = nums[3];
// 没出问题catch就不走
} catch(Exception ex) { // 捕获所有异常
// 打印出错误的相关信息
ex.printStackTrace();
//
logger.error("数组下标越界了!!");
} finally { // 不管是否出现异常,都要执行
System.out.println("程序正常终止!");
}
}
1.8、数据持久化
1.8.1、学生序列化
public class Demo2 {
public static void main(String[] args) {
// 把学生对象进行持久化,保存到我们的硬盘里面
// 占用N个字节
Student stu = new Student("叶孤城" ,20 );
// 创建一个IO流对象(就是哪个水管,水管里面的数据就是字节/字符),用于把数据发送到硬盘的某个文件里面
// output单词:输出 - 》 把内存里面的数据输出到硬盘的文件里面,文件的名称是什么?
try {
// 猜一下,执行这款代码,我的d盘会不会创建stu.obj
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("d:/stu.obj"));
// 把学生对象写入到stu.obj文件里面
out.writeObject(stu);
System.out.println("学生对象写入到文件成功!");
}catch (Exception ex) {
ex.printStackTrace();
}
}
}
1.8.2、把员工对象保存到本地
需求:
编写一个writeEmp方法,用junit启动,然后把员工对象序列化到磁盘的emp.obj文件中。
1、transient关键字
使用transient关键字修饰的属性不会被序列化
2、序列化:ObjectOutputStream
/**
* 员工类
*/
public class Employee implements Serializable {
// 4个属性,我希望密码不被序列化
private int id; // 编号
private String name; // 姓名
private double salary; // 薪资
// transient 关键字修饰的属性,不会被序列化!!
// 当比较敏感的字段可以使用
// 金额,积分,密码,身份证.....
private transient String pwd; // 登录密码
public Employee(int id, String name, double salary, String pwd) {
this.id = id;
this.name = name;
this.salary = salary;
this.pwd = pwd;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
", salary=" + salary +
", pwd='" + pwd + '\'' +
'}';
}
public double getSalary() {
return salary;
}
}
// 写到磁盘。输出 . 序列化
@Test
public void writeEmp() {
Scanner input = new Scanner(System.in);
System.out.println("员工编号:");
int id = input.nextInt();
System.out.println("员工姓名:");
String name = input.next();
System.out.println("员工薪资:");
double salary = input.nextDouble();
System.out.println("员工密码:");
String pwd = input.next();
Employee emp = new Employee(id, name, salary, pwd);
// 序列化到本地的emp.obj
ObjectOutputStream out = null;
try {
out = new ObjectOutputStream(new FileOutputStream("d:/emp.obj"));
out.writeObject(emp); // 把对象写入到文件中
System.out.println("员工对象序列化成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
out.close(); // 关闭流,释放资源。必须调用
System.out.println("关闭流成功,释放资源!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
1.8.3、把员工对象从本地读取
需求:
编写一个readEmp方法,用junit启动。把磁盘的emp.obj读取进来,转换为Employee对象并打印。
反序列化:
// 从磁盘中读取文件到内存。输入。 反序列化
@Test
public void readEmp() throws IOException, ClassNotFoundException {
/* 如果修改了类,要记得重新写一次 */ // alt+回车
ObjectInputStream in = new ObjectInputStream(new FileInputStream("d:/emp.obj"));
Object obj = in.readObject(); // 读取到内存里面。返回值是Object
Employee emp = (Employee)obj; // 强转
System.out.println("读取成功,对象的信息是:");
System.out.println(emp);
in.close(); // 关闭流,释放字眼
}
1.8.4、保存一批员工对象到本地
需求:
编写一个writeEmps方法,用junit启动,然后把员工对象序列化到磁盘的emps.obj文件中。
// 保存一批员工对象到本地
@Test
public void writeEmps() throws FileNotFoundException {
ArrayList<Employee> list = new ArrayList<>();// 集合保存员工对象 。 集合也是个对象
Scanner input = new Scanner(System.in); // 扫描器对象,一个就够用了
while (true){
System.out.println("员工编号:");
int id = input.nextInt();
System.out.println("员工姓名:");
String name = input.next();
System.out.println("员工薪资:");
double salary = input.nextDouble();
System.out.println("员工密码:");
String pwd = input.next();
Employee emp = new Employee(id, name, salary, pwd);
// 每创建一个员工对象,马上就加到集合里面去....
list.add(emp);
System.out.println("是否继续:");
String ans = input.next();
if (ans.equals("no")) break;
}
/*******很臃肿。try-catch不能加太多了******/
ObjectOutputStream out = null;
try {
// 保存到本地的emps.mp3
// 创建一个输出流对象
out = new ObjectOutputStream(new FileOutputStream("emps.mp3"));
out.writeObject(list);
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("保存一批对象成功,同时资源关闭!");
}
1.8.5、把一批员工对象又从本地读取
需求:
编写一个readEmps方法,用junit启动,把一批员工对象从本地读取进来,然后按照薪资从高到低排序。
@Test
public void readEmps() throws IOException, ClassNotFoundException {
// 先读进来,再来排序。
ObjectInputStream in = new ObjectInputStream(new FileInputStream("emps.mp3"));
// list集合,其实也是一个list集合
Object o = in.readObject();
// 转换
ArrayList<Employee> list = (ArrayList<Employee>)o;
// 排序使用比较器Compatator
Collections.sort(list, (o1, o2) -> (int)(o2.getSalary() - o1.getSalary())); // 降序
// 写法二: 既排序又输出
list.stream().sorted((o1, o2) -> (int)(o2.getSalary() - o1.getSalary())).forEach((System.out::println));
System.out.println("编号\t\t姓名\t\t薪资待遇");
list.forEach(System.out::println);
in.close();
}
1.9、读取记事本的内容
1.9.1、使用字符串读取
// 读取china记事本的内容 (读文件内容)
// 我们这个版本是初级版本,一个字符一个字符读的,也最好理解
@Test
public void readChina() throws IOException {
// 1、准备记事本
// 创建一个可以按照字符读取的流对象.就是那一条绿色的管子
FileReader reader = new FileReader("d:/china.txt");
int r = -1;
//调一次read,返回一个字符
// 效率不是特别高 , 要循环26次,要读26次
while ( (r = reader.read()) != -1) {
System.out.print((char)r);
}
reader.close(); // 关闭流,释放资源
}
// 升级版2.0,一次性读取多个字符!
// 缺点:扩展性不好。 希望定义一个合适(也不大,也不小)大小的容器,能读取任何长度的内容
// V3.0
@Test
public void readChina2() throws IOException {
FileReader r = new FileReader("d:/china.txt");
// 准备一个容器,用来容纳存储的字符,一共可容纳26个
// 虽然是30,但是实际是只读了26-(N)个字符。。
char[] buffers = new char[30];
// 怎么知道读取的字符实际个数N呢?
int n = r.read(buffers); // 读的时候,把读的所有字符存到buffers数组里面去
// 把字符串数组转换为字符串
String str = String.valueOf(buffers , 0 , n);
System.out.println(str);
r.close();
}
// 优点:扩展性好。 定义一个合适(也不大,也不小)大小的容器,能读取任何长度的内容
// V3.0 - > 最终版
@Test
public void readChina3() throws IOException {
long start = System.currentTimeMillis(); // 开始时间(毫秒)
FileReader reader = new FileReader("d:/china.txt");
char[] buffers = new char[1024];
int n = 0;
int count = 0;
// 添加循环
while((n = reader.read(buffers)) != -1) {
// 把字符串数组转换为字符串
String str = String.valueOf(buffers , 0 , n);
System.out.println(str);
count += n; // 叠加实际读取的字符个数
}
reader.close();
long end = System.currentTimeMillis(); // 结束时间
System.out.println("共耗时:" + (end - start) + "毫秒");
System.out.println("一共有"+count+"个字。");
}
1.9.2、使用字节读取
@Test
public void read() throws IOException {
// 昨天是字符, 今天是字节。
// 记事本里面存的都是字符
// 准备一个管道,通过字节来传送!!!
FileInputStream in = new FileInputStream("d:/china.txt");
int c = in.read(); // 读取1个字节
System.out.println(c); // 97 , 既陌生(没看出来是a),又熟悉(ascii码 , a对应的97)
System.out.println((char)c);
System.out.println("----------以下是中文!!-----------");
// 用字节读取中文的时候,尽量不要去打印,不要去看!! 不看就不乱码, 看了就乱码!!
int c2 = in.read(); // 读 中文: 实 - > 229
System.out.println(c2);
System.out.println((char)c2); // å (它是什么东西? 它是半个实 , 半个字)
System.out.println("---------------------------------");
in.close();
}
@Test
public void read2() throws IOException {
// 昨天是字符, 今天是字节。
// 记事本里面存的都是字符
// 准备一个管道,通过字节来传送!!!
FileInputStream in = new FileInputStream("d:/china.txt");
byte[] bufs = new byte[1024]; // 可以容量3个字节
// 昨天是返回的是实际读取的字符个数, 今天呢是读取的字节个数
int len = -1;
while ( (len = in.read(bufs)) != -1) {
//新知识点, 把字节数组转换为String,方便查看
// String(byte[] bytes, int offset, int length)
// 有3个格子,假设只读取了2个字节,所以我们只需要拿两个格子就行了,剩下的哪个不要
String str = new String(bufs , 0 , len);
System.out.println(str);
}
in.close();
}
1.10、爬虫
1.10.1、爬取图片并拷贝到本地
// 图片的拷贝
@Test
public void copyImage() throws IOException {
// 需求:把电脑上任意位置的图片,拷贝到项目工程里面。
// 因为图片是二进制文件,所以我们不能使用FileReader,使用FileInputStream
// 第一步:创建了一个管道输入流对象!! (绿色的)
FileInputStream in = new FileInputStream("d:/images/2.jpg");
// 创建一个管道,输出流对象!! (蓝色的)
FileOutputStream out = new FileOutputStream("妹子.jpg");
// 第二步读
byte[] bufs = new byte[1024];
int len = -1; // 保存每一次读取的字节个数
// 一边读read,一边写write
while ((len = in.read(bufs)) != -1) { // 图片是一个整体,再读取过程中不能去看的
out.write(bufs , 0 , len); // 输出
}
in.close(); // 关闭流
out.close(); // 关闭流
}
// 网络编程。
// 从互联网上通过URL地址进行网络下载图片
// URL 也叫做统一资源定位符: 作用就是定位到互联网上的某台服务器(百度公司)的某个资源(这个图片)
@Test
public void downloadImage() throws IOException /*抛出以表示发生格式不正确的网址。*/ {
String urlStr = "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg9.51tietu.net%2Fpic%2F2019-090917%2F1mdtumszxxk1mdtumszxxk.jpg&refer=http%3A%2F%2Fimg9.51tietu.net&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1665978666&t=1d707ceb6734a9440d36c8e68e879997";
// 创建一个URL对象
URL url = new URL(urlStr); // 这个类是Java之父, 詹姆斯.高斯林写的 , 1.0版本
// 根据URL开启一个网络连接. 网络连接也是个对象, URLConnection
URLConnection urlConnection = url.openConnection();
System.out.println("连接成功!!");
// 开辟一个管道 -》 通过流的形式进行传输
// 这句代码:其实就是拿到哪个输入流(绿色的)
InputStream in = urlConnection.getInputStream();
FileOutputStream out = new FileOutputStream("5.jpg");
byte[] bufs = new byte[1024];
int len = -1;
while ((len = in.read(bufs)) != -1 ) {
out.write(bufs , 0 , len);
}
out.close();
in.close();
System.out.println("从网络上下载图片成功!!!");
}
2、回顾
2.1、编写一个递归,输出1到10的阶乘
public class Test1 {
public static void main(String[] args) {
DiGui();
}
public static void DiGui(int i){
if(i>10){
return;
}
System.out.println(i+"+"+i+"="+i*i);
}
}
2.2、编写一个List集合,使用迭代器对象来遍历集合
2.2.1、迭代器: Iterator
public class Test2 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("sss");
list.add("dddw");
//通过迭代器对象来完成
Iterator iterator = list.iterator(); // 获取一个迭代器对象
ListIterator<String> itr = list.listIterator();//也是通过迭代器遍历 优点:指针可以向前、向后
System.out.println(itr.next());//向前
System.out.println(itr.previous());//向后
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
2.3、实现商品价格从高到底排序
/**
* 商品类
*/
class Shop {
private int id; // 编号
private String name; // 商品名称
private int price; // 价格
private int sales; // 销量
// 全参 - 》 方便初始化
public Shop(int id, String name, int price, int sales) {
this.id = id;
this.name = name;
this.price = price;
this.sales = sales;
}
@Override
public String toString() {
return id + "\t" + name + "\t\t" + price + "\t" + sales;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public int getSales() {
return sales;
}
public void setSales(int sales) {
this.sales = sales;
}
}
public class Demo1 {
public static void main(String[] args) {
List<Shop> list = new ArrayList<>();
list.add(new Shop(1 , "Iphone14 Pro" , 8700 , 5000));
list.add(new Shop(2 , "SK-II" , 2510 , 9000));
list.add(new Shop(3 , "thinkpad x1 carbon" , 11000 , 4000));
list.add(new Shop(4 , "天梭(TISSOT)瑞士手表" , 4350 , 1500));
/*价格从高到底排序,所以要用比较器
* 1、Compatable - > 和类进行绑定的 , 必须实现接口
* 2、Comparator -> 不和类进行绑定,很灵活
* */
// 工具类Collections (集合工具类)
// 写法一:传统工艺写法。 1.8之前大家都是这样用的
/* Collections.sort(list, (Shop o1, Shop o2) -> {
return o2.getPrice() - o1.getPrice();
}
);*/
System.out.println("编号\t\t名称\t\t价格\t\t销量");
// 写法二:不使用工具类。 1.8 - 》 Lambda , 流(链式写法- 》 对象.方法.方法.方法....)
// 返回此集合中元素的顺序流(集合->流)
list.stream().sorted(( o1, o2) -> o2.getPrice() - o1.getPrice()).forEach(System.out::println);
System.out.println("------------------------------------------------------------------------");
// 从低到高
list.stream().sorted(( o1, o2) -> o1.getPrice() - o2.getPrice()).forEach(System.out::println);
}
}
2.4、定义一个方法conver 能实现小转大,大转小
public class Demo2 {
public static void main(String[] args) {
// 2、定义一个方法conver 能实现小转大,大转小
String str = "AbCd";
// 选中一段代码,自动生成方法定义: alt+ctrl+m
String converStr = convert(str);
System.out.println("转换前:" + str);
System.out.println("转换后:" + converStr);
}
// 快速修改方法名: shift + f6
private static String convert(String str) {
char[] chars = str.toCharArray(); // 把字符串打碎,转换为char数组。
for (int i = 0; i < chars.length; i++) {
char c = chars[i]; // 通过索引访问数组元素,存到字符c里面
// 怎么知道是大写还是小写呢?
// 推断(两种方式): 如果c是大写的,那么和c的大写形式进行比较,true就是大写,否则就是小写
// 如果c是小写的,那么和c的小写形式进行比较,trur就是小写,否则就是大写
// public static char toUpperCase(char ch) // 把字符转大写
if (c == Character.toUpperCase(c)) {
// 证明c是大写的
// c已经是大写的,转小写
chars[i] = Character.toLowerCase(c);
} else {
chars[i] = Character.toUpperCase(c);
}
}
// 把char数组转换为String (新知识点!)
// String.valueOf: 返回包含字符数组的字符的字符串。
String converStr = String.valueOf(chars);
return converStr;
}
}
3、项目实战
3.1、吃货联盟
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner input = new Scanner(System.in);
// 初始化3个数组存储菜品、价格、点赞数
String[] dishNames = { "红烧带鱼", "鱼香肉丝", "时令蔬菜" };
double[] prices = { 38.0, 20.0, 10.0, };
int[] praiseNums = new int[5];
// 在初始化6个数组用于存储订单信息
String[] names = new String[5];// 保存订餐人姓名
String[] dishMegs = new String[5];// 保存所选菜品名
int[] dishNums = new int[5];// 保存所选菜品份数
int[] times = new int[5];// 保存送餐时间
String[] addresses = new String[5];// 保存送餐地址
int[] states = new int[5];// 保存订单状态:0表示已预订,1表示已完成
double[] sumPrices = new double[5];// 保存订单的总金额
// 初始化2条订单信息
// 第1条
names[0] = "张三";
dishMegs[0] = "鱼香肉丝";
dishNums[0] = 2;
times[0] = 12;
addresses[0] = "成华区建设路";
states[0] = 0;
sumPrices[0] = 40.0;
// 第2条
names[1] = "李四";
dishMegs[1] = "时令蔬菜";
dishNums[1] = 1;
times[1] = 19;
addresses[1] = "成都东站";
states[1] = 1;
sumPrices[1] = 10.0;
/* 实现菜单切换 */
System.out.println("欢迎使用“吃货联盟订餐系统”");
int num = 1;// 用户输入0返回主菜单否则退出系统
boolean isExit = false;// 标记用户是否退出系统,true为退出
// 循环控制用户输入选择并执行
do {
System.out.println("************************");
System.out.println("1、我要订餐");
System.out.println("2、查看餐袋");
System.out.println("3、签收订单");
System.out.println("4、删除订单");
System.out.println("5、我要点赞");
System.out.println("6、退出系统");
System.out.println("************************");
System.out.print("请选择:");
num = input.nextInt();// 用户输入所要选择进行的操作
switch (num) {
case 1:// 我要订餐
System.out.println("***我要订餐***");
boolean isAdd = false;// 记录是否可以点餐
for (int i = 0; i < names.length; i++) {
if (names[i] == null) {
isAdd = false;
System.out.print("请输入订餐人姓名:");
String name = input.next();
System.out.println("序号" + "\t" + "菜名" + "\t\t\t" + "单价" + "\t\t" + "点赞数");
for (int j = 0; j < dishNames.length; j++) {
System.out.println(
(j + 1) + "\t" + dishNames[j] + "\t\t" + prices[j] + "元" + "\t" + praiseNums[j]);
}
System.out.print("请选择您要点的菜品编号:");
int chooseDish = input.nextInt();
String dishMeg = dishNames[chooseDish - 1];
System.out.print("请选择您需要的份数:");
int number = input.nextInt();
System.out.print("请输入送餐时间(送餐时间是10点至20点间整点送餐):");
int time = input.nextInt();
while (time < 10 || time > 20) {
System.out.print("您的输入有误,请输入10-20间的整数");
time = input.nextInt();
}
System.out.print("请输入送餐地址:");
String address = input.next();
// 默认可以预定,不用写订单状态
System.out.println("订餐成功!");
System.out.println("您定的是:" + dishNames[chooseDish - 1] + number + "份");
System.out.println("送餐时间:" + time + "点");
double sumPrice = prices[chooseDish - 1] * number;// 计算餐费
double sendMoney = (sumPrice >= 50) ? 0 : 5;// 计算送餐费
System.out.println("餐费:" + sumPrice + "元," + "送餐费" + sendMoney + "元," + "总计:"
+ (sumPrice + sendMoney) + "元。");
// 添加数据
names[i] = name;
dishMegs[i] = dishMeg;
times[i] = time;
dishNums[i] = number;
addresses[i] = address;
sumPrices[i] = sumPrice + sendMoney;
break;
}
if (isAdd) {
System.out.println("对不起,您的餐袋已满!");
}
}
break;
case 2:// 查看餐袋
System.out.println("***查看餐袋***");
System.out.println("序号\t订餐人\t餐品名称\t餐品数量\t送餐时间\t送餐地址\t总金额\t订单状态");
for (int i = 0; i < names.length; i++) {
if (names[i] != null) {
String state = (states[i] == 0) ? "已预订" : "已完成";
System.out.println((i + 1) + "\t" + names[i] + "\t" + dishMegs[i] + "\t" + dishNums[i] + "份"
+ "\t" + times[i] + "\t" + addresses[i] + "\t" + sumPrices[i] + "元" + "\t" + state);
}
}
break;
case 3:// 签收订单
System.out.println("***签收订单***");
boolean isSignFind = false;
System.out.print("请选择要签收的订单序号:");
int sign = input.nextInt();
for (int i = 0; i < names.length; i++) {
if (names[i] != null && states[i] == 0 && sign == i + 1) {
states[i] = 1;
System.out.println("订单签收成功!");
isSignFind = true;// 标记已找到的订单
} else if (names[i] != null && states[i] == 1 && sign == i + 1) {
System.out.println("您选择的订单已完成签收,不能再次签收!");
isSignFind = true;// 标记已找到的订单
}
}
if (!isSignFind) {
System.out.println("您选择的订单不存在!");
}
break;
case 4:// 删除订单
System.out.println("***删除订单***");
boolean isDelFind = false;
System.out.print("请输入要删除的订单序号:");
int delID = input.nextInt();
for (int i = 0; i < names.length; i++) {
// 根据状态值判断能不能删除,只有已完成的才能删除
if (names[i] != null && states[i] == 1 && delID == i + 1) {
isDelFind = true;
for (int j = delID - 1; j < names.length - 1; j++) {
names[j] = names[j + 1];
dishMegs[j] = dishMegs[j + 1];
dishNums[j] = dishNums[j + 1];
times[j] = times[j + 1];
addresses[j] = addresses[j + 1];
states[j] = states[j + 1];
sumPrices[j] = sumPrices[j + 1];
}
// 将最后一位清空
names[names.length - 1] = null;
dishMegs[names.length - 1] = null;
dishNums[names.length - 1] = 0;
times[names.length - 1] = 0;
addresses[names.length - 1] = null;
states[names.length - 1] = 0;
sumPrices[names.length - 1] = 0;
System.out.println("删除订单成功!");
break;
} else if (names[i] != null && states[i] == 0 && delID == i + 1) {
isDelFind = true;
System.out.println("您选择的订单未签收,不能删除!");
break;
}
} // 未找到的订单不能删除
if (!isDelFind) {
System.out.println("您要删除的订单不存在!");
}
break;
case 5:// 我要点赞
System.out.println("***我要点赞***");
// 显示菜品信息
System.out.println("序号\t菜名\t单价");
for (int i = 0; i < dishNames.length; i++) {
String priaiseNum = (praiseNums[i] > 0) ? praiseNums[i] + "赞" : "";
System.out.println((i + 1) + "\t" + dishNames[i] + "\t" + prices[i] + "元" + priaiseNum);
}
System.out.print("请选择您要点赞的菜品序号:");
int number = input.nextInt();
praiseNums[number - 1]++;
System.out.println("点赞成功");
break;
case 6:// 退出系统
isExit = true;
break;
default:// 退出系统
isExit = true;
break;
}
// 返回主界面
if (!isExit) {
System.out.print("输入0返回:");
num = input.nextInt();
} else {
break;
}
} while (num == 0);
System.out.println("谢谢惠顾,欢迎您再次使用!");
input.close();
}
}
3.2、ATM
/*
用户类
T000 这个类没设计好,应该把卡和用户进行分开
*/
public class User {
private int cardid;
private String uname;
public String pwd;
private double blance;
public User(){
}
public User(int cardId,String uname,String pwd,double blance){
this.cardid=cardId;//卡号
this.uname=uname;//用户名
this.pwd=pwd;//密码
this.blance=blance;//余额
}
public int getCardid() {
return cardid;
}
public void setCardid(int cardid) {
this.cardid = cardid;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public double getBlance() {
return blance;
}
public void setBlance(double blance) {
this.blance = blance;
}
}
/*
这个一个接口
*/
public interface ATM {
/**
* 登录ATM
* @param cardId 卡号
* @param pwd 密码
* @return true代表登录成功,false代表登录失败
*/
public boolean login(int cardId,String pwd);
/**
* 存钱
* @param m 存多少前
*/
public void save(double m);
/**
* 取钱
* @param m 取多少钱
* @return true表示取款成功 false表示取款失败
*/
public boolean qu(double m);
//查询所有用户
public void findAll();
//查询余额
public double find();
/**
* 转账
* @param cardId 转账账户
* @param uname 接受转账账户
* @param m 转账金额
* @return
*/
public boolean transfer(int cardId, String uname, double m);
/**
*
* @param newPwd 修改后的新密码
* @return
*/
public boolean savePwd(String newPwd);
}
/*
接口实现类
*/
public class ATMImplement implements ATM {
//初始化3个账户,存放在集合里
private List<User> list = new ArrayList<>();
//当前登录的用户
//登录成功后,
private User currentUser;
public static String bankName = "成都华夏银行";
public ATMImplement() {
//进行初始化
User u1 = new User(001, "路飞", "123456", 2000);
User u2 = new User(002, "露娜", "zhe111", 4500);
User u3 = new User(003, "索隆", "zxc147", 5100);
list.add(u1);//把账户添加在集合里
list.add(u2);
list.add(u3);
}
@Override
public boolean login(int cardId, String pwd) {
//默认false,登录失败
boolean r = false;
//登录就是将控制台输入的卡号和密码保存起来,然后在对所有账户对象进行比较,如果对的上就登录成功
for (User user : list) {
if (user.getCardid() == cardId && user.getPwd().equals(pwd)) {
r = true;
currentUser = user;//将登录成功的用户的地址赋值给currentUser
break;
}
}
return r;
}
@Override
public void save( double m) {
currentUser.setBlance(currentUser.getBlance()+m);
}
@Override
public boolean qu( double m) {
boolean r = false;
if(m > currentUser.getBlance()){
r = true;
System.out.println("您的余额已不足");
}else {
currentUser.setBlance(currentUser.getBlance() -m);
System.out.println("取款成功!!!");
}
return r;
}
@Override
public void findAll() {
//遍历所有账户集合
for (User user:list) {
StringBuilder builder = new StringBuilder();
builder.append("卡号:").append(user.getCardid());
builder.append("\t\t用户名:").append(user.getUname());
builder.append("\t\t密码:").append(user.getPwd());
builder.append("\t\t余额:").append(user.getBlance());
System.out.println(builder.toString());
}
}
@Override
public double find() {
double balance = currentUser.getBlance();
return balance;
}
@Override
public boolean transfer(int cardId, String uname, double m) {
// -------------------以下操作是个原子操作(要么全部成功,要么全部失败,失败就要回滚),涉及到事务--------
// 举个例子: 比如你刚把钱转出去,自己的账户已经扣除了.. 突然网络出问你了,系统卡死了...
// 要判断余额是否满足 && 对方的账户是否存在!!
// 自己账户减
// 对方账号加
// ------------------------------------------------------
//判断余额是否满足
if (m > currentUser.getBlance()) {//余额不足
System.out.println("余额不足,转账失败");
return false;
}
User targetUser = new User();//对方接收转账的账户对象
for (User user : list) {
if (user.getCardid() == cardId && user.getUname().equals(uname)) {
targetUser = user;
break;
}
}
if(targetUser == null ){//如果转账用户为null,则返回false
System.out.println("输入转账用户有误");
return false;
}
currentUser.setBlance(currentUser.getBlance() - m);
targetUser.setBlance(currentUser.getBlance() + m);
return true;
}
@Override
public boolean savePwd(String newPwd) {
currentUser.pwd = newPwd;
currentUser.setPwd(currentUser.pwd);
return true;
}
}
/*
测试类
*/
public class Test {
public static <simpDateFormat> void main(String[] args) {
Scanner input = new Scanner(System.in);
ATM atm = new ATMImplement();//创建一个ATM对象
atm.findAll();//登录前显示所有用户
//开始登录,最多有3次机会
int count = 3;//默认输入密码的次数只有3次
do {
count--;
System.out.print("请输入银行卡:");
int cardId = input.nextInt();
System.out.print("请输入密码:");
String pwd = input.next();
boolean r = atm.login(cardId,pwd);//显示登录验证,如果为true表示登录成功
if (r) {
//进入系统,就进行下面的循环
//只要没退卡就可以连续操作
while (true){
System.out.println("=====================================");
System.out.println(" || 1.查询余额 5.修改密码 ||");
System.out.println(" || 2.取款 6.退卡 ||");
System.out.println(" || 3.存款 ||");
System.out.println(" || 4.转账 ||");
System.out.println(" =====================================");
System.out.print("请选择要操作的号码:");
int n = input.nextInt();
if(n==1){//查询余额
double balance = atm.find();
System.out.println("余额有"+balance);
}else if(n==2){
System.out.print("请输入取款金额:");
double m = input.nextDouble();
boolean result = atm.qu(m);
double balance = atm.find();
if (result) {
System.out.println("=====================================");
System.out.println("|| 1.返回主界面 2.打印发票||");
System.out.println("=====================================");
// TODO
// 获取当前时间
System.out.print("请输入要操作的数字:");
int chooes = input.nextInt();
if(chooes==1){
return;
}else if(chooes==2){
System.out.println("当前余额"+balance);
Date d = new Date(); // 要显示 年月日:时分秒格式
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String f = format.format(d);
System.out.println(f);
}
}
}else if(n==3){
System.out.print("请输入要存款的金额:");
double m = input.nextDouble();
atm.save(m);
System.out.println("哔!哔!哔! 正在放钞");
System.out.println("存款成功!!!");
}else if(n==4){
System.out.print("请输入要转账的用户:");
String uname = input.next();
System.out.print("请输入要转账的金额:");
double m = input.nextDouble();
boolean result = atm.transfer(cardId,uname,m);
if(result){
double balance = atm.find();
System.out.print("您是否确定转账给该用户(y/n):");
String chooes = input.next();
if(chooes.equalsIgnoreCase("y")){
System.out.println("转账成功");
System.out.println("当前余额"+balance);
}else if(chooes.equalsIgnoreCase("n")){
System.out.println("放弃转账");
break;
}
}
}else if(n==5){
System.out.print("请输入六位的密码:");
String newPwd = input.next();
boolean re =atm.savePwd(newPwd);
if(re){
if(pwd.equals(newPwd)){
System.out.println("修改后的密码不可与当前密码一样");
}else if(newPwd == null || newPwd.length() != 6){
System.out.println("输入密码有误!");
}else {
System.out.println("修改成功");
count = 3;
break;
}
}
}else if(n==6){
System.out.println("欢迎下次使用");
}else if(n <= 0 || n > 6){
System.out.println("输入有误,请重写输入");
continue;
}
}
} else {
if (count > 0) {
System.out.println("还有" + count + "次机会");
} else {
System.out.println("卡已锁定,请到人工咨询处处理");
}
}
}while (count > 0 );
}
}
4、难点补充
4.1、谈接口与抽象类的区别?
封装:抽取对象共有的属性,然后编写类
类是抽象的,对象就具体的
//抽象类和接口的区别
//抽象类必须要有一个子类来继承,通过子类来创建实例(继承单一继承)
//接口一个类可以实现多个接口,弥补单一继承的问题
//抽象类继承体系 -》is - a -> 名词
//接口实例体系(能力) -》has -a -> 动词
abstract class Vei{
//交通工具
}
class Car extends Vei implements Run,Fir{
//汽车属于交通工具,汽车是交通工具
@Override
public void run() {
System.out.println("地上跑");
}
@Override
public void fir() {
System.out.println("天上跑");
}
}
interface Run{
//运输,是交通工具的一种能力
void run();
}
interface Fir{
void fir();
}
public class Demo {
}
4.2、调用函数时,形参的改变能否影响实参?用实验证明您的观点
4.2.1、形参:方法定义的参数就是形参
4.2.2、实参:调用方法传参的实际值就是实参
public class Demo2 {
//值传递
public static void f1(int i/*形参*/){
i++;
System.out.println(i);//打印输出21
}
//引用传递,nums拿到的是地址
public static void f2(int[] nums/*形参*/){
//nums[0] = 999;//改的是形参
//new关键字是在堆区重新开辟一块新的空间
nums = new int[3];
nums[0] = 999;
nums[1] = 888;
nums[2] = 777;
System.out.println(Arrays.toString(nums));
}
//引用传递(String除外)
public static void f3(Car c){
c.color= "白色";
System.out.println("汽车的颜色是:"+c.color);
}
public static void main(String[] args) {
int num = 20;
f1(num/*实参*/);
System.out.println(num);//打印输出20
//存的是地址
int[] nums = {3,1,2};
f2(nums/*实参*/);
System.out.println(Arrays.toString(nums));
Car c = new Car();
c.color = "黑色";
f3(c);
System.out.println("汽车的颜色是:"+c.color);
}
4.3、什么是多态
// 参数类型是汽车, 汽车加速 , c 运行的时候类型是不是就是飞机。。。。
// 动态是java中最重要的特性 - 》 -》
// 动态绑定机制
// 有了多态- 》 抽象工厂(造对象)的时候,就特别的方便
public static void f1(Vei c) {
c.up(); // 调用方法产生不同的结果了。
}
// 传子类对象
public static void f2(抽象类类型 参数) {
}
// 传实现类对象
public static void f2(接口类型 参数) {
}
public static void main(String[] args) {
// 向上转型
f1(new Car());
}
4.4、log4j日志框架记录错误信息到本地
操作步骤:1)拷贝3个jar包到我们的工程里面。
2)编写核心配置 log4j.properties
3)创建日志记录对象Logger
# 格式非常的统一 key = 值
log4j.rootLogger=DEBUG,console,file
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout=org.apache.log4j.PatternLayout
# 控制台的内容格式
log4j.appender.console.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss} method:%l%n%m%n
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=D://logs/log.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
# 控制文件里面的内容格式
log4j.appender.file.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss} method:%l%n%m%n
4.5、junit单元测试框架
4.5.框架的基本含义
已经写好的一套程序,我们只管引入进来用就可以了!java中的框架大多是以.jar包发布的。
- Spring框架
- Mybatis框架
- Hibernate框架
4.5.2、Junit单元测试框架的作用
-
系统上线前,要做大量的测试 。(内测1个月时间)
-
接口写好了,可以来测试接口是否完整。
-
如果一个程序没有经过大量的测试,几乎不可能正常运行
@Test 用它标记的方法,就是一个程序入口了!!
@Before 用它标记的方法,再程序入口执行前执行!
@After 用它标记的方法,再程序入口执行完后执行!
import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; public class Demo1 { String name; /* show2方法之前执行 */ @Before public void show1() { // 1 name = "张三"; System.out.println("show1方法执行了"); } @After public void show3() { // 3 System.out.println("show3方法执行了!"); } // 数据库 , 再查询之前要先创建一个连接管道 // 使用注解: @注解名 @Test public void show2() { // 这个方法必须是public的,不带参数的 // 2 System.out.println("show2"); System.out.println(name); // 断言 - 》可以做出判断, 如果为true给你一个绿色的勾勾 , false红色叉叉 // 判断对象是否为空 String str = null; Assert.assertNull(str); // 判断某个对象是不是为null,为null就true //Assert.assertNotNull(str); // 判断某个对象是不是不为null Assert.assertTrue(5 > 3); // 判断表达式是否为真,为真就正确 Assert.assertEquals("abc" , "abcd"); } }
5、面试题
5.1、每个字符出现的次数
需求:
1)定义一个需要被统计字符的字符串
2)将字符串转换成字符数组
3)定义Map集合,用来存储字符串中字符以及字符出现的次数
4)遍历字符数组获取每一个字符,并将字符存储在双列集合中,存储过程中要做判断,如果集合中不包含这个键,就将该字符当做键,值为1存储,如果集合中包含这个键,就将值加1存储
5)打印Map集合获取字符出现的次数
public class Demo5 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("请输入一个字符串:");
String tx = input.next();
HashMap<Character, Integer> countMap = count(tx);
for (Map.Entry<Character, Integer> row : countMap.entrySet()) {
System.out.println("字符" + row.getKey() + "出现的次数是:" + row.getValue());
}
/* 新的需求:统计出现字符次数最多的哪个字符!! 找最大值,擂台算法 */
}
// 字符串统计出现个的数,返回一个HashMap,key是字符,value是次数
private static HashMap<Character , Integer> count(String tx /*假设:abc*/) {
HashMap<Character, Integer> map = new HashMap<>();
char[] chars = tx.toCharArray(); // 把字符串转换为char数组,方便统计
for (char c : chars) { // 遍历数组
Integer num = map.get(c); // 根据字符获取对应的value,当然如果第一次循环,集合里面是空的,所以拿出来的num为null
if (num == null) {
map.put(c , 1); // 存
} else {
map.put(c , num+1);
}
}
return map;
}
}
5.2、如何保证类的对象有且只有一个【使用单列模式】
5.2.1、饿汉式 (直接new)
/**
* Created by ${wuyupku} on 2019/3/15 12:39
*/
class Singleton01{
private static Singleton01 modle = new Singleton01();//声明对象同时私有化
private Singleton01(){}//构造函数私有化
public static Singleton01 getInstance(){//向外声明访问该类对象的方法
return modle;
}
}
5.2.2、懒汉式 (再方法里面再来new)
/**
* Created by ${wuyupku} on 2019/3/15 12:43
*/
class Singleton02 {
private static Singleton02 modle = null;//声明对象,不实例化
private Singleton02() {}//构造函数私有化
public static Singleton02 getInstance(){//向外提供访问该类对象的方法
if (modle == null)
modle = new Singleton02();
return modle;
}
}
5.2.3、设计模式(共24个)
1、工厂模式:造对象
2、代理:横切面问题
3、单例:类只有一个对象
4、装饰:对象包装
5、观察者:事件驱动
// 无论如何,Person类只能产生一个对象
// 外部人家想new就new了, 一旦new就创建了新的对象了
// 问题第一层: 阻止外部new , 就是不要外部来调用构造方法!!
// 问题第二层:既然外部都不能创建对象了,那么只能由类来自己控制
/**
* 单例设计模式
* 原理:把构造方法私有化,然后内部提供一个内类对象的实例,有且只有1个
*/
public class Person {
// 要造多少个对象由Person来决定
// static 还有映像不?
static Person p = new Person();
// 当把构造方法修饰为private后,外部就不能再创建该类对象了!!
private Person() {
}
}
Person p = Person.p;
Person p2 = Person.p;
System.out.println(p == p2);
// 无论如何,Person类只能产生一个对象
// 外部人家像new就new了, 一旦new就创建了新的对象了
// 问题第一层: 阻止外部new , 就是不要外部来调用构造方法!!
// 问题第二层:既然外部都不能创建对象了,那么只能由类来自己控制
/**
* 单例设计模式 - > 饿汉式 .
* 比我这个方法还要好的话:詹姆斯.高斯林 提供了一种解决方案 , Enum枚举来实现!
* 原理:把构造方法私有化,然后内部提供一个内类对象的实例,有且只有1个
*/
public class Person {
// 要造多少个对象由Person来决定
// static 还有映像不?
private static Person p = new Person();
// getter - > 这种方式是最好了,因为了减少了麻烦
// 饿汉式
public static Person getPerson() {
return p;
}
// 懒汉式
public static Person getPerson2() {
// TODO 多个线程同时访问,拿到的p是不同的!!
if (p == null) {
p = new Person();
}
return p;
}
// 当把构造方法修饰为private后,外部就不能再创建该类对象了!!
private Person() {
}
}
public class Dem4 {
public static void main(String[] args) {
//new就创建一个对象
//Person p1 = new Person();private之后new就报错了
//阻止外部new,一new就报错
Person p1 = Person.getPerson();//通过类名调用对象属性
Person p2 = Person.getPerson();
System.out.println(p == p2);
}
}
5.3、final的作用,尤其是修饰高级类型对象时,什么可变什么不可变?【对象地址可不变,堆区值可变】
5.3.1、final修饰的变量就是常量,不可变
public class Demo5 {
public static void main(String[] args) {
//1年12个月
final int month = 12;//基本数据类型不可变
/**
* 引用数据类型比较特殊
* 英语数据类型的对象,地址不可变,里面存储的值可以变
*/
final int[] nums = new int[3];
//nums的值不可变
nums = new int[5];
//同时又可以变
nums[0]=999;
}
}
5.3.2、final优点【不可大量使用】
final修饰的方法,还有属性变量等,会被JVM进行优化,效率更高
final修饰的属性变量等,可以防止线程数据安全等问题
5.3.3、final修饰的类不能继承
5.3.4、final修饰的方法不可被重写
5.4、排序为什么要用比较器,比较器中重写的那个方法会被触发1次还是多次?该片段对整个排序有何作用?
5.4.1、什么是比较器
Comparable、Comparator
5.4.2、为什么要使用比较器
1、首先比较器的目的是为了排序而生, 当有多个对象排序才显得比较有意义。
2、如果不用比较器,我们每一套业务场景,比如学生要按照年龄排序,商品要按照价格进行排序,员工要按照岗位级别及进行排序, 这种情况都需要自己编写一套排序算法,然后还要进行大量的交换位置操作,并且代码还不能复用,非常不利用团队协作。
3、比较器出现的价值,就是为了解决不同业务场景下排序复用的问题,因为比较器内部把排序的交换等核心算法已经封装好了, 我们只管填充排序的业务逻辑即可,比如学生就按照年龄进行比较,商品就按照价格进行比较。
4、最后比较器因为是接口,所以我们可以编写多套排序场景的实现,根据场景的不同进行切换,这样就非常有利于程序的可扩展性。
5.4.5、==、equal()、hashcode()区别
==:数值判断
equal():字符判断
hashcode():获取哈希码,根据关键码值(Key value)而直接进行访问的数据结构
5.4.6、String的hashcode()原理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JhkNi0UN-1664470090398)(E:\笔记\第四周\202209132235372.png)]
5.4.7、try catch语句里面写return会不会继续执行,finally呢?
finally:是必须执行的
// TODO 多个线程同时访问,拿到的p是不同的!!
if (p == null) {
p = new Person();
}
return p;
}
// 当把构造方法修饰为private后,外部就不能再创建该类对象了!!
private Person() {
}
}
public class Dem4 {
public static void main(String[] args) {
//new就创建一个对象
//Person p1 = new Person();private之后new就报错了
//阻止外部new,一new就报错
Person p1 = Person.getPerson();//通过类名调用对象属性
Person p2 = Person.getPerson();
System.out.println(p == p2);
}
}
#### 5.3、final的作用,尤其是修饰高级类型对象时,什么可变什么不可变?【对象地址可不变,堆区值可变】
##### 5.3.1、final修饰的变量就是常量,不可变
```Java
public class Demo5 {
public static void main(String[] args) {
//1年12个月
final int month = 12;//基本数据类型不可变
/**
* 引用数据类型比较特殊
* 英语数据类型的对象,地址不可变,里面存储的值可以变
*/
final int[] nums = new int[3];
//nums的值不可变
nums = new int[5];
//同时又可以变
nums[0]=999;
}
}
5.3.2、final优点【不可大量使用】
final修饰的方法,还有属性变量等,会被JVM进行优化,效率更高
final修饰的属性变量等,可以防止线程数据安全等问题
5.3.3、final修饰的类不能继承
5.3.4、final修饰的方法不可被重写
5.4、排序为什么要用比较器,比较器中重写的那个方法会被触发1次还是多次?该片段对整个排序有何作用?
5.4.1、什么是比较器
Comparable、Comparator
5.4.2、为什么要使用比较器
1、首先比较器的目的是为了排序而生, 当有多个对象排序才显得比较有意义。
2、如果不用比较器,我们每一套业务场景,比如学生要按照年龄排序,商品要按照价格进行排序,员工要按照岗位级别及进行排序, 这种情况都需要自己编写一套排序算法,然后还要进行大量的交换位置操作,并且代码还不能复用,非常不利用团队协作。
3、比较器出现的价值,就是为了解决不同业务场景下排序复用的问题,因为比较器内部把排序的交换等核心算法已经封装好了, 我们只管填充排序的业务逻辑即可,比如学生就按照年龄进行比较,商品就按照价格进行比较。
4、最后比较器因为是接口,所以我们可以编写多套排序场景的实现,根据场景的不同进行切换,这样就非常有利于程序的可扩展性。
5.4.5、==、equal()、hashcode()区别
==:数值判断
equal():字符判断
hashcode():获取哈希码,根据关键码值(Key value)而直接进行访问的数据结构
5.4.6、String的hashcode()原理
5.4.7、try catch语句里面写return会不会继续执行,finally呢?
finally:是必须执行的
try catch:在try或catch中的return语句会将它的返回值压入栈内,然后执行finally语句,当finally执行完成后,若finally语句里有return语句,则执行return语句并结束。