饥人谷java体系课

面向对象的三大特征:封装、继承、多态

封装与访问控制

封装

隐藏内部实现细节,只暴露出接口
好处:只需要修改接口中的内部细节,不用修改其他地方使用的这些接口

封装的实现、访问控制符

public 任何⼈都能访问
protected 只有⼦类可以访问和同⼀个包的可以访问
包是没有嵌套、包含关系的!!!!
package private 只有同⼀个包的类可以访问
private 只有⾃⼰可以访问

getter和setter与javaBean约定

getter

可以得到这个属性的方法

setter

可以设置这个属性的方法

JavaBean约定

对getter、setter方法名的约定
非boolean属性:getter方法 get属性名
setter方法 set属性名
boolean属性 getter方法 is属性名
setter方法 set属性名

JSON:将对象用字符串的方式表示出来的方法
JSON<—>java Object
对json操作的时候,只看javaBean中getter、setter方法的名字,而不看实现细节的名字


序列化和反序列化
JSON maven依赖

  1. <dependency>
  2. <groupId>com.alibaba</groupId>
  3. <artifactId>fastjson</artifactId>
  4. <version>x.x.x</version>
  5. </dependency>

其中 x.x.x 是版本号,根据需要使用特定版本,建议使用最新版本。
JSONAPI
可以使用 JSON.toJSONString() 将 Java 对象转换换为 JSON 对象:
JSON.parseObject(json,Student.class); 将json转换为java对象

  1. public class Main {
  2. /*
  3. 假设你正在为学校开发一个学生分数记录系统
  4. 你和前端约定的JSON接口格式是:
  5. {
  6. "name": "张三",
  7. "retakingExam": true,
  8. "score": 59,
  9. "fail": true // 是否挂科,如果分数低于60则返回true,代表挂科
  10. }
  11. 请:
  12. 1. 设计并完成Student类
  13. 2. 挑选一种你喜欢的JSON类库,完成序列化/反序列化的方法
  14. */
  15. public static void main(String[] args) {
  16. Student student = new Student();
  17. student.setName("张三");
  18. student.setScore(60);
  19. student.setRetakingExam(true);
  20. String json = serialize(student);
  21. System.out.println(json);
  22. student = deserialize(json);
  23. }
  24. // 序列化:将Student类转换成JSON字符串
  25. public static String serialize(Student student) {
  26. return JSON.toJSONString(student);
  27. }
  28. // 反序列化:将JSON字符串转换成Student对象
  29. public static Student deserialize(String json) {
  30. return JSON.parseObject(json,Student.class);
  31. }
  32. }
  1. public class Student {
  2. // 请按照Main类的要求,补全本类
  3. /** 姓名 */
  4. private String name;
  5. /** 是否重考。true为重考,falase为非重考。 */
  6. private boolean retakingExam;
  7. /** 分数 */
  8. private int score;
  9. private boolean fail;
  10. public String getName() {
  11. return name;
  12. }
  13. public void setName(String name) {
  14. this.name = name;
  15. }
  16. public boolean isRetakingExam() {
  17. return retakingExam;
  18. }
  19. public void setRetakingExam(boolean retakingExam) {
  20. this.retakingExam = retakingExam;
  21. }
  22. public int getScore() {
  23. return score;
  24. }
  25. public void setScore(int score) {
  26. this.score = score;
  27. }
  28. public boolean isFail() {
  29. return fail;
  30. }
  31. public void setFail(boolean fail) {
  32. this.fail = fail;
  33. }
  34. }

工厂模式

《Effective java》
使用静态工厂方法代替构造器
私有化构造器

  1. public class Cat {
  2. private String name;
  3. private boolean cute;
  4. public boolean isCute() {
  5. return cute;
  6. }
  7. public void setCute(boolean cute) {
  8. this.cute = cute;
  9. }
  10. public String getName() {
  11. return name;
  12. }
  13. public void setName(String name) {
  14. this.name = name;
  15. }
  16. private Cat(String name,boolean cute) {
  17. this.name = name;
  18. this.cute=cute;
  19. }
  20. public static Cat newCuteCat(String name){
  21. if (name==null){
  22. return null;
  23. }
  24. return new Cat(name,true);
  25. }
  26. }

优点:

不像构造器,他是有名字的;可以描述这个方法再做什么
不像构造器,不需要去创建一个新的对象
可以返回,返回类型的子类型,提高静态工厂方法的灵活性
可以根据传递进来的参数,决定要不要创建对象以及创建什么样的对象
静态工厂返回的对象可以不存在

缺点:

没有办法被子类化
很难让开发者找到

  1. public class Cat {
  2. private static final Cat INVALID_CAT = new Cat("Invalid cat", -1);
  3. private String name;
  4. private int age;
  5. //私有化构造器
  6. private Cat(String name, int age) {
  7. this.name = name;
  8. this.age = age;
  9. }
  10. /**
  11. * 创建一只猫的工厂方法。当传入的参数无效,即:
  12. *
  13. * <p>1. age小于0 2. name是空字符串或者null时
  14. *
  15. * <p>返回预先创建好的{@link #INVALID_CAT};
  16. *
  17. * <p>否则,返回一只新创建的猫
  18. *
  19. * @param age 年龄
  20. * @param name 名字
  21. * @return 创建的猫
  22. */
  23. public static Cat newCat(String name, int age) {
  24. if (age<0||"".equals(name)||name==null){
  25. return INVALID_CAT;
  26. }
  27. return new Cat(name,age);
  28. }
  29. public String getName() {
  30. return name;
  31. }
  32. public int getAge() {
  33. return age;
  34. }
  35. }

类的访问控制符

public 任何都能访问
package private 包级私有,前面什么都不写,只能在同一个包中访问
private inner class 内部类,只能在同一个类中访问

java的模块系统 Java Platfrom Module System

限制当前类只能在当前模块中访问,降低耦合,易于修改

builder模式

会定义每个属性设置的方法和和其相关的方法名,便于设置该属性

  1. public class UserBuilder {
  2. // 请在这里使用builder模式建造User对象
  3. // 所需的接口请参阅UserBuilderTest测试类
  4. private String firstName;
  5. private String lastName;
  6. private String phoneNumber;
  7. private String address;
  8. public UserBuilder(){
  9. }
  10. public static UserBuilder anUser(){
  11. return new UserBuilder();
  12. }
  13. public UserBuilder firstName(String firstName){
  14. this.firstName=firstName;
  15. return this;
  16. }
  17. public UserBuilder lastName(String lastName){
  18. this.lastName=lastName;
  19. return this;
  20. }
  21. public UserBuilder phoneNumber(String phoneNumber){
  22. this.phoneNumber=phoneNumber;
  23. return this;
  24. }
  25. public UserBuilder address(String address){
  26. this.address=address;
  27. return this;
  28. }
  29. public User build(){
  30. return new User(firstName,lastName,phoneNumber,address);
  31. }
  32. }
  1. public class User {
  2. /** 用户的名 */
  3. private final String firstName;
  4. /** 用户的姓 */
  5. private final String lastName;
  6. /** 用户的电话 */
  7. private final String phoneNumber;
  8. /** 用户的地址 */
  9. private final String address;
  10. public String getFirstName() {
  11. return firstName;
  12. }
  13. public String getLastName() {
  14. return lastName;
  15. }
  16. public String getPhoneNumber() {
  17. return phoneNumber;
  18. }
  19. public String getAddress() {
  20. return address;
  21. }
  22. User(String firstName, String lastName, String phoneNumber, String address) {
  23. this.firstName = firstName;
  24. this.lastName = lastName;
  25. this.phoneNumber = phoneNumber;
  26. this.address = address;
  27. }

组合与继承

继承

关键字 extends
单根继承
所有类都是Object的子类

Object的toString()和equals()方法

toString():将对象转为字符串
equals()和==的区别
equals():方法本质也是==,在其他子类中可以被重写,可以根据重写的方法进行比较
==比较的是值,基本数据类型就比较其本身大小,但是对象直接比较的是地址。

继承中的类结构和初始化顺序

子类拥有父类的一切数据和行为
父类先于子类
必须有匹配的构造器

实例方法的覆盖 Override

又称为覆盖\重写
永远使⽤@Override注解

super关键字

可以调用父类的方法

设计模式:模板方法

提供⼀个“模板”,实现可以覆盖模板的全部或者部分

  1. public class Story {
  2. public final void tellStory() {
  3. startStory();
  4. story();
  5. endStory();
  6. }
  7. public void startStory() {
  8. System.out.println("开始讲故事啦");
  9. }
  10. public void story() {
  11. System.out.println("从前有个老和尚");
  12. }
  13. public void endStory() {
  14. System.out.println("故事讲完啦");
  15. }
  16. public static void main(String[] args) {
  17. new Story().tellStory();
  18. }
  19. }
  1. //1.继承Story类
  2. public class MonsterStory extends Story {
  3. // 请补全本类,使得main方法可以输出以下内容:
  4. //
  5. // 开始讲故事啦
  6. // 从前有个老妖怪
  7. // 故事讲完啦
  8. // 你还想听吗
  9. //重写需要修改的方法
  10. @Override
  11. public void story() {
  12. System.out.println("从前有个老妖怪");
  13. }
  14. //重写方法,并调用父类的该方法
  15. @Override
  16. public void endStory() {
  17. super.endStory();
  18. System.out.println("你还想听吗");
  19. }
  20. public static void main(String[] args) {
  21. new MonsterStory().tellStory();
  22. }
  23. }

向上、向下转型

一个子类类型的对象永远是一个父类类型的对象

instanceof 判断类型

判断一个地址是不是某一个类的对象实例,还可以判断某一个对象实例是不是实现了某个接口
null instanceof xxx ==false 右边一定是一个类
当需要⼀个⽗类型时,总可以传递⼀个⼦类型
向下转型有可能是不安全的,需要强制类型转换

final关键字与单例模式

final声明变量,变量成为不可变的(必须初始化)
局部变量/⽅法参数
成员变量
常量与单例
final修饰的变量只能被赋值一次
final修饰的好处:可以保证它是线程安全的,因为它不会被改变
final修饰的对象指向的地址是不能改变的,当对象中的数据是可以改变的
final在⽅法上的声明:禁⽌继承/覆盖/重写此⽅法
final在类声明上的使⽤:禁⽌继承此类
String、Integer都是final修饰的,不可被继承

单例模式

  1. public class SingleObject {
  2. //创建 SingleObject 的一个对象
  3. private static SingleObject instance = new SingleObject();
  4. //让构造函数为 private,这样该类就不会被实例化
  5. private SingleObject(){}
  6. //获取唯一可用的对象
  7. public static SingleObject getInstance(){
  8. return instance;
  9. }
  10. public void showMessage(){
  11. System.out.println("Hello World!");
  12. }
  13. }
  1. public class SingletonPatternDemo {
  2. public static void main(String[] args) {
  3. //不合法的构造函数
  4. //编译时错误:构造函数 SingleObject() 是不可见的
  5. //SingleObject object = new SingleObject();
  6. //获取唯一可用的对象
  7. SingleObject object = SingleObject.getInstance();
  8. //显示消息
  9. object.showMessage();
  10. }
  11. }

组合

在类中创建想要继承的对象,让这个类拥有它而不是是它

多态

允许将子类类型的指针赋值给父类类型的指针,把不同的子类对象都当作父类来看
实例⽅法默认是多态的
在运⾏时根据this来决定调⽤哪个⽅法
静态⽅法没有多态
参数静态绑定,接收者动态绑定
多态只选择接受者的类型,不选择参数的类型

  1. public class Base {
  2. public void print(BaseParam param){
  3. System.out.println("i am base,the param is baseParam");
  4. }
  5. public void print(SubParam param){
  6. System.out.println("i am base,the param is subParam");
  7. }
  8. }
  9. public class Sub extends Base{
  10. @Override
  11. public void print(SubParam param) {
  12. System.out.println("i am sub,the param is subParam");
  13. }
  14. @Override
  15. public void print(BaseParam param) {
  16. System.out.println("i am sub,the param is baseParam");
  17. }
  18. }
  19. public class BaseParam {
  20. }
  21. public class SubParam extends BaseParam{
  22. }
  1. public class Main {
  2. public static void main(String[] args) {
  3. Base base = new Sub();
  4. BaseParam param=new SubParam();
  5. base.print(param);
  6. }
  7. }//输出结果:i am sub,the param is baseParam

设计模式:策略模式

  1. public class PriceCalculator {
  2. // 使用策略模式重构这个方法,实现三个策略:
  3. // NoDiscountStrategy 不打折
  4. // Discount95Strategy 全场95折
  5. // OnlyVipDiscountStrategy 只有VIP打95折,其他人保持原价
  6. // 重构后的方法签名:
  7. // public static int calculatePrice(DiscountStrategy strategy, int price, User user)
  8. public static int calculatePrice(DiscountStrategy strategy, int price, User user) {
  9. return strategy.discount(price, user);
  10. }
  11. }
  1. public class DiscountStrategy {
  2. public int discount(int price, User user) {
  3. throw new UnsupportedOperationException();
  4. }
  5. }
  1. public class NoDiscountStrategy extends DiscountStrategy{
  2. @Override
  3. public int discount(int price, User user) {
  4. return price;
  5. }
  6. }
  1. public class Discount95Strategy extends DiscountStrategy{
  2. @Override
  3. public int discount(int price, User user) {
  4. return (int) (price * 0.95);
  5. }
  6. }
  1. public class OnlyVipDiscountStrategy extends DiscountStrategy{
  2. @Override
  3. public int discount(int price, User user) {
  4. if (user.isVip()) {
  5. return (int) (price * 0.95);
  6. } else {
  7. return price;
  8. }
  9. }
  10. }

抽象类与接口

抽象类

abstract声明的类
抽象类可以有普通类的所有东西
可以声明抽象方法,抽象方法必须声明在抽象类里
不可实例化
可以实例化的东⻄⼀定要补全所有的⽅法体。
可以包含抽象⽅法 - ⾮private/static
可以包含普通类的任何东⻄

接口

接口不是类,只代表一种功能
类实现接口 implements 关键字
一个类只能继承一个类,但能实现多个接口
所有实现接口的类都必须实现接口的所有方法,因此,接口在使用之后就不可修改
打破向后兼容性

接口可以包含

若⼲个⽅法(默认public)
若⼲个常量(默认public static final)
extends 接⼝
默认⽅法
java8之后引入default默认接口方法,其不是实现接口必须实现的方法
同一个类实现不同的接口中有同名方法时,编译时就会给出警告

什么时候使用接口,什么时候使用抽象类

复用代码的时候用抽象类,描述一个功能的时候使用接口

接口和抽象方法的异同

同:1.都是抽象的 2.不可实例化 3.可以包含抽象方法(没有方法体,非ststic、private、final的)
不同:1.抽象类是类,可以包含类的一切东西,但是接口只能包含受限的成员和方法,java8之后可以加default的默认方法
2.抽象类只能单一继承,而接口是可以多继承的,甚至继承多次

接口实战:comparable

  1. package com.github.hcsp.polymorphism;
  2. import java.io.IOException;
  3. import java.util.Arrays;
  4. import java.util.Collections;
  5. import java.util.List;
  6. public class Point implements Comparable<Point>{
  7. private final int x;
  8. private final int y;
  9. // 代表笛卡尔坐标系中的一个点
  10. public Point(int x, int y) {
  11. this.x = x;
  12. this.y = y;
  13. }
  14. public int getX() {
  15. return x;
  16. }
  17. public int getY() {
  18. return y;
  19. }
  20. @Override
  21. public boolean equals(Object o) {
  22. if (this == o) {
  23. return true;
  24. }
  25. if (o == null || getClass() != o.getClass()) {
  26. return false;
  27. }
  28. Point point = (Point) o;
  29. if (x != point.x) {
  30. return false;
  31. }
  32. return y == point.y;
  33. }
  34. @Override
  35. public int hashCode() {
  36. int result = x;
  37. result = 31 * result + y;
  38. return result;
  39. }
  40. @Override
  41. public String toString() {
  42. return String.format("(%d,%d)", x, y);
  43. }
  44. // 按照先x再y,从小到大的顺序排序
  45. // 例如排序后的结果应该是 (-1, 1) (1, -1) (2, -1) (2, 0) (2, 1)
  46. public static List<Point> sort(List<Point> points) {
  47. Collections.sort(points);
  48. return points;
  49. }
  50. public static void main(String[] args) throws IOException {
  51. List<Point> points =
  52. Arrays.asList(
  53. new Point(2, 0),
  54. new Point(-1, 1),
  55. new Point(1, -1),
  56. new Point(2, 1),
  57. new Point(2, -1));
  58. System.out.println(Point.sort(points));
  59. }
  60. @Override
  61. public int compareTo(Point point) {
  62. if (this.x>point.x){
  63. return 1;
  64. }else if (this.x<point.x){
  65. return -1;
  66. }
  67. if (this.y>point.y){
  68. return 1;
  69. }else if (this.y<point.y){
  70. return -1;
  71. }
  72. return 0;
  73. }
  74. }

内部类

⽤途:实现更加精细的封装
可以访问外围类的实例⽅法
⾮静态内部类
和⼀个外围类实例相绑定
可以访问外围类实例的⽅法
静态内部类
不和外围类实例相绑定
不可以访问外围实例的⽅法
原则:永远使⽤静态内部类,除⾮编译报错

匿名内部类

直接通过new的方式创建无名类

文件过滤器

  1. package com.github.hcsp.polymorphism;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.nio.file.*;
  5. import java.nio.file.attribute.BasicFileAttributes;
  6. import java.util.ArrayList;
  7. import java.util.List;
  8. public class FileFilter {
  9. public static void main(String[] args) throws IOException {
  10. Path projectDir = Paths.get(System.getProperty("user.dir"));
  11. Path testRootDir = projectDir.resolve("test-root");
  12. if (!testRootDir.toFile().isDirectory()) {
  13. throw new IllegalStateException(testRootDir.toAbsolutePath().toString() + "不存在!");
  14. }
  15. List<String> filteredFileNames = filter(testRootDir, ".csv");
  16. System.out.println(filteredFileNames);
  17. }
  18. /**
  19. * 实现一个按照扩展名过滤文件的功能
  20. *
  21. * @param rootDirectory 要过滤的文件夹
  22. * @param extension 要过滤的文件扩展名,例如 .txt
  23. * @return 所有该文件夹(及其后代子文件夹中)匹配指定扩展名的文件的名字
  24. */
  25. public static List<String> filter(Path rootDirectory, String extension) throws IOException {
  26. List<String> fileName = new ArrayList<>();
  27. Files.walkFileTree(rootDirectory, new SimpleFileVisitor<Path>() {
  28. @Override
  29. public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
  30. if (file.getFileName().toString().endsWith(extension)) {
  31. fileName.add(file.getFileName().toString());
  32. }
  33. return FileVisitResult.CONTINUE;
  34. }
  35. });
  36. return fileName;
  37. }
  38. }