饥人谷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依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>x.x.x</version>
</dependency>
其中 x.x.x 是版本号,根据需要使用特定版本,建议使用最新版本。
JSONAPI
可以使用 JSON.toJSONString() 将 Java 对象转换换为 JSON 对象:
JSON.parseObject(json,Student.class); 将json转换为java对象
public class Main {
/*
假设你正在为学校开发一个学生分数记录系统
你和前端约定的JSON接口格式是:
{
"name": "张三",
"retakingExam": true,
"score": 59,
"fail": true // 是否挂科,如果分数低于60则返回true,代表挂科
}
请:
1. 设计并完成Student类
2. 挑选一种你喜欢的JSON类库,完成序列化/反序列化的方法
*/
public static void main(String[] args) {
Student student = new Student();
student.setName("张三");
student.setScore(60);
student.setRetakingExam(true);
String json = serialize(student);
System.out.println(json);
student = deserialize(json);
}
// 序列化:将Student类转换成JSON字符串
public static String serialize(Student student) {
return JSON.toJSONString(student);
}
// 反序列化:将JSON字符串转换成Student对象
public static Student deserialize(String json) {
return JSON.parseObject(json,Student.class);
}
}
public class Student {
// 请按照Main类的要求,补全本类
/** 姓名 */
private String name;
/** 是否重考。true为重考,falase为非重考。 */
private boolean retakingExam;
/** 分数 */
private int score;
private boolean fail;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isRetakingExam() {
return retakingExam;
}
public void setRetakingExam(boolean retakingExam) {
this.retakingExam = retakingExam;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
public boolean isFail() {
return fail;
}
public void setFail(boolean fail) {
this.fail = fail;
}
}
工厂模式
《Effective java》
使用静态工厂方法代替构造器
私有化构造器
public class Cat {
private String name;
private boolean cute;
public boolean isCute() {
return cute;
}
public void setCute(boolean cute) {
this.cute = cute;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private Cat(String name,boolean cute) {
this.name = name;
this.cute=cute;
}
public static Cat newCuteCat(String name){
if (name==null){
return null;
}
return new Cat(name,true);
}
}
优点:
不像构造器,他是有名字的;可以描述这个方法再做什么
不像构造器,不需要去创建一个新的对象
可以返回,返回类型的子类型,提高静态工厂方法的灵活性
可以根据传递进来的参数,决定要不要创建对象以及创建什么样的对象
静态工厂返回的对象可以不存在
缺点:
没有办法被子类化
很难让开发者找到
public class Cat {
private static final Cat INVALID_CAT = new Cat("Invalid cat", -1);
private String name;
private int age;
//私有化构造器
private Cat(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 创建一只猫的工厂方法。当传入的参数无效,即:
*
* <p>1. age小于0 2. name是空字符串或者null时
*
* <p>返回预先创建好的{@link #INVALID_CAT};
*
* <p>否则,返回一只新创建的猫
*
* @param age 年龄
* @param name 名字
* @return 创建的猫
*/
public static Cat newCat(String name, int age) {
if (age<0||"".equals(name)||name==null){
return INVALID_CAT;
}
return new Cat(name,age);
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
类的访问控制符
public 任何都能访问
package private 包级私有,前面什么都不写,只能在同一个包中访问
private inner class 内部类,只能在同一个类中访问
java的模块系统 Java Platfrom Module System
builder模式
会定义每个属性设置的方法和和其相关的方法名,便于设置该属性
public class UserBuilder {
// 请在这里使用builder模式建造User对象
// 所需的接口请参阅UserBuilderTest测试类
private String firstName;
private String lastName;
private String phoneNumber;
private String address;
public UserBuilder(){
}
public static UserBuilder anUser(){
return new UserBuilder();
}
public UserBuilder firstName(String firstName){
this.firstName=firstName;
return this;
}
public UserBuilder lastName(String lastName){
this.lastName=lastName;
return this;
}
public UserBuilder phoneNumber(String phoneNumber){
this.phoneNumber=phoneNumber;
return this;
}
public UserBuilder address(String address){
this.address=address;
return this;
}
public User build(){
return new User(firstName,lastName,phoneNumber,address);
}
}
public class User {
/** 用户的名 */
private final String firstName;
/** 用户的姓 */
private final String lastName;
/** 用户的电话 */
private final String phoneNumber;
/** 用户的地址 */
private final String address;
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getPhoneNumber() {
return phoneNumber;
}
public String getAddress() {
return address;
}
User(String firstName, String lastName, String phoneNumber, String address) {
this.firstName = firstName;
this.lastName = lastName;
this.phoneNumber = phoneNumber;
this.address = address;
}
组合与继承
继承
Object的toString()和equals()方法
toString():将对象转为字符串
equals()和==的区别
equals():方法本质也是==,在其他子类中可以被重写,可以根据重写的方法进行比较
==比较的是值,基本数据类型就比较其本身大小,但是对象直接比较的是地址。
继承中的类结构和初始化顺序
实例方法的覆盖 Override
super关键字
设计模式:模板方法
提供⼀个“模板”,实现可以覆盖模板的全部或者部分
public class Story {
public final void tellStory() {
startStory();
story();
endStory();
}
public void startStory() {
System.out.println("开始讲故事啦");
}
public void story() {
System.out.println("从前有个老和尚");
}
public void endStory() {
System.out.println("故事讲完啦");
}
public static void main(String[] args) {
new Story().tellStory();
}
}
//1.继承Story类
public class MonsterStory extends Story {
// 请补全本类,使得main方法可以输出以下内容:
//
// 开始讲故事啦
// 从前有个老妖怪
// 故事讲完啦
// 你还想听吗
//重写需要修改的方法
@Override
public void story() {
System.out.println("从前有个老妖怪");
}
//重写方法,并调用父类的该方法
@Override
public void endStory() {
super.endStory();
System.out.println("你还想听吗");
}
public static void main(String[] args) {
new MonsterStory().tellStory();
}
}
向上、向下转型
instanceof 判断类型
判断一个地址是不是某一个类的对象实例,还可以判断某一个对象实例是不是实现了某个接口
null instanceof xxx ==false 右边一定是一个类
当需要⼀个⽗类型时,总可以传递⼀个⼦类型
向下转型有可能是不安全的,需要强制类型转换
final关键字与单例模式
final声明变量,变量成为不可变的(必须初始化)
局部变量/⽅法参数
成员变量
常量与单例
final修饰的变量只能被赋值一次
final修饰的好处:可以保证它是线程安全的,因为它不会被改变
final修饰的对象指向的地址是不能改变的,当对象中的数据是可以改变的
final在⽅法上的声明:禁⽌继承/覆盖/重写此⽅法
final在类声明上的使⽤:禁⽌继承此类
String、Integer都是final修饰的,不可被继承
单例模式
public class SingleObject {
//创建 SingleObject 的一个对象
private static SingleObject instance = new SingleObject();
//让构造函数为 private,这样该类就不会被实例化
private SingleObject(){}
//获取唯一可用的对象
public static SingleObject getInstance(){
return instance;
}
public void showMessage(){
System.out.println("Hello World!");
}
}
public class SingletonPatternDemo {
public static void main(String[] args) {
//不合法的构造函数
//编译时错误:构造函数 SingleObject() 是不可见的
//SingleObject object = new SingleObject();
//获取唯一可用的对象
SingleObject object = SingleObject.getInstance();
//显示消息
object.showMessage();
}
}
组合
多态
允许将子类类型的指针赋值给父类类型的指针,把不同的子类对象都当作父类来看
实例⽅法默认是多态的
在运⾏时根据this来决定调⽤哪个⽅法
静态⽅法没有多态
参数静态绑定,接收者动态绑定
多态只选择接受者的类型,不选择参数的类型
public class Base {
public void print(BaseParam param){
System.out.println("i am base,the param is baseParam");
}
public void print(SubParam param){
System.out.println("i am base,the param is subParam");
}
}
public class Sub extends Base{
@Override
public void print(SubParam param) {
System.out.println("i am sub,the param is subParam");
}
@Override
public void print(BaseParam param) {
System.out.println("i am sub,the param is baseParam");
}
}
public class BaseParam {
}
public class SubParam extends BaseParam{
}
public class Main {
public static void main(String[] args) {
Base base = new Sub();
BaseParam param=new SubParam();
base.print(param);
}
}//输出结果:i am sub,the param is baseParam
设计模式:策略模式
public class PriceCalculator {
// 使用策略模式重构这个方法,实现三个策略:
// NoDiscountStrategy 不打折
// Discount95Strategy 全场95折
// OnlyVipDiscountStrategy 只有VIP打95折,其他人保持原价
// 重构后的方法签名:
// public static int calculatePrice(DiscountStrategy strategy, int price, User user)
public static int calculatePrice(DiscountStrategy strategy, int price, User user) {
return strategy.discount(price, user);
}
}
public class DiscountStrategy {
public int discount(int price, User user) {
throw new UnsupportedOperationException();
}
}
public class NoDiscountStrategy extends DiscountStrategy{
@Override
public int discount(int price, User user) {
return price;
}
}
public class Discount95Strategy extends DiscountStrategy{
@Override
public int discount(int price, User user) {
return (int) (price * 0.95);
}
}
public class OnlyVipDiscountStrategy extends DiscountStrategy{
@Override
public int discount(int price, User user) {
if (user.isVip()) {
return (int) (price * 0.95);
} else {
return price;
}
}
}
抽象类与接口
抽象类
abstract声明的类
抽象类可以有普通类的所有东西
可以声明抽象方法,抽象方法必须声明在抽象类里
不可实例化
可以实例化的东⻄⼀定要补全所有的⽅法体。
可以包含抽象⽅法 - ⾮private/static
可以包含普通类的任何东⻄
接口
接口不是类,只代表一种功能
类实现接口 implements 关键字
一个类只能继承一个类,但能实现多个接口
所有实现接口的类都必须实现接口的所有方法,因此,接口在使用之后就不可修改
打破向后兼容性
接口可以包含
若⼲个⽅法(默认public)
若⼲个常量(默认public static final)
extends 接⼝
默认⽅法
java8之后引入default默认接口方法,其不是实现接口必须实现的方法
同一个类实现不同的接口中有同名方法时,编译时就会给出警告
什么时候使用接口,什么时候使用抽象类
接口和抽象方法的异同
同:1.都是抽象的 2.不可实例化 3.可以包含抽象方法(没有方法体,非ststic、private、final的)
不同:1.抽象类是类,可以包含类的一切东西,但是接口只能包含受限的成员和方法,java8之后可以加default的默认方法
2.抽象类只能单一继承,而接口是可以多继承的,甚至继承多次
接口实战:comparable
package com.github.hcsp.polymorphism;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Point implements Comparable<Point>{
private final int x;
private final int y;
// 代表笛卡尔坐标系中的一个点
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Point point = (Point) o;
if (x != point.x) {
return false;
}
return y == point.y;
}
@Override
public int hashCode() {
int result = x;
result = 31 * result + y;
return result;
}
@Override
public String toString() {
return String.format("(%d,%d)", x, y);
}
// 按照先x再y,从小到大的顺序排序
// 例如排序后的结果应该是 (-1, 1) (1, -1) (2, -1) (2, 0) (2, 1)
public static List<Point> sort(List<Point> points) {
Collections.sort(points);
return points;
}
public static void main(String[] args) throws IOException {
List<Point> points =
Arrays.asList(
new Point(2, 0),
new Point(-1, 1),
new Point(1, -1),
new Point(2, 1),
new Point(2, -1));
System.out.println(Point.sort(points));
}
@Override
public int compareTo(Point point) {
if (this.x>point.x){
return 1;
}else if (this.x<point.x){
return -1;
}
if (this.y>point.y){
return 1;
}else if (this.y<point.y){
return -1;
}
return 0;
}
}
内部类
⽤途:实现更加精细的封装
可以访问外围类的实例⽅法
⾮静态内部类
和⼀个外围类实例相绑定
可以访问外围类实例的⽅法
静态内部类
不和外围类实例相绑定
不可以访问外围实例的⽅法
原则:永远使⽤静态内部类,除⾮编译报错
匿名内部类
直接通过new的方式创建无名类
文件过滤器
package com.github.hcsp.polymorphism;
import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;
public class FileFilter {
public static void main(String[] args) throws IOException {
Path projectDir = Paths.get(System.getProperty("user.dir"));
Path testRootDir = projectDir.resolve("test-root");
if (!testRootDir.toFile().isDirectory()) {
throw new IllegalStateException(testRootDir.toAbsolutePath().toString() + "不存在!");
}
List<String> filteredFileNames = filter(testRootDir, ".csv");
System.out.println(filteredFileNames);
}
/**
* 实现一个按照扩展名过滤文件的功能
*
* @param rootDirectory 要过滤的文件夹
* @param extension 要过滤的文件扩展名,例如 .txt
* @return 所有该文件夹(及其后代子文件夹中)匹配指定扩展名的文件的名字
*/
public static List<String> filter(Path rootDirectory, String extension) throws IOException {
List<String> fileName = new ArrayList<>();
Files.walkFileTree(rootDirectory, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (file.getFileName().toString().endsWith(extension)) {
fileName.add(file.getFileName().toString());
}
return FileVisitResult.CONTINUE;
}
});
return fileName;
}
}