1、面向对象程序设计
三大特性:
1. 封装
- 隐藏对象的属性和实现细节,进对外提供公共访问方式,保护内部操作不被破坏
2. 继承
- 在原本的基础之上继续进行拓展,提高代码的复用性,是多态的前提
3. 多态
- 父类或接口定义的引用变量可以指向子类或具体实现类的实例对象。
五大原则:
1. 单一职责原则SRP(Single Responsibility Principle)
- 功能要单一
2. 开放封闭原则OCP(Open-Close Principle)
- 开放拓展,拒绝修改
3. 里式替换原则LSP(the Liskov Substitution Principle LSP)
- 子类可以替换父类出现父类能出现的任何地方
4. 依赖倒置原则DIP(the Dependency Inversion Principle DIP)
- 高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象
5. 接口分离原则ISP(the Interface Segregation Principle ISP)
- 设计时采用多个与特定客户类有关的接口比采用一个通用的接口要好
2、类、对象、引用定义
1. 类
- 是对具有相同特征和行为的多个对象共性的抽象描述,里面包含了描述属性的成员变量和描述行为的成员方法
2. 对象
- 对象是一种个性的表示,是一个独立的隔离,依靠属性来区分不同对象
总接:类是对象的模板,对象是类的实例。在开发中应先产生类,后产生对象。类不能直接使用,而对象可以直接使用
案例:打印当前月日历
import java.time.LocalDate;
/**
* @author AlanLiang
* @date 2021/7/29
*/
public class CalendarTest {
public static void main(String[] args) {
//1.创建LocalDate对象
LocalDate date = LocalDate.now();
//2.调用对象方法获取 月份和号数
int monthValue = date.getMonthValue();
int today = date.getDayOfMonth();
//3.将date改为当前月的第一天
date = date.minusDays(today - 1);
//获取当月的第一天是星期几
int value = date.getDayOfWeek().getValue();
//4.打印日期首行
System.out.println("Mon Tue Wed Thu Fri Sat Sun");
//5.因为要与日历的星期几对其,前面需空行,遍历value值
for (int i = 0; i < value; i++) {
System.out.print(" ");
}
//打印号数
//判断是否在当月,进入循环
while (date.getMonthValue() == monthValue){
//打印号数,格式化
System.out.printf("%3d",date.getDayOfMonth());
if (date.getDayOfMonth() == today){
System.out.print("*");
}else {
System.out.print(" ");
}
//每打一天后,date+1
date = date.plusDays(1);
//当date是周一时,换行
if (date.getDayOfWeek().getValue() == 1){
System.out.println();
}
}
}
}
3、对自定义类剖析
案例:自定义类Employee
import java.time.LocalDate;
/**
* @author l3725
*/
public class EmployeeTest {
public static void main(String[] args) {
//创建职工数组
Employee[] employees = new Employee[3];
employees[0] = new Employee("小明",1000.00,1995,2,20);
employees[1] = new Employee("小红",2000.00,1996,3,20);
employees[2] = new Employee("小白",3000.00,1997,4,20);
//每个人涨5%工资
for (Employee employee : employees) {
employee.raiseSalary(5);
}
//打印所有员工信息
for (Employee employee : employees) {
System.out.println("name="+employee.getName()+", salary="+employee.getSalary()+", hireDay="+employee.getHireDay());
}
}
}
/**
- 封装Employee类
*/
class Employee {
//定义属性
private String name;
private double salary;
private LocalDate hireDay;
public Employee() {
}
public Employee(String name, double salary, int year, int month, int day) {
this.name = name;
this.salary = salary;
setHireDay(year,month,day);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public LocalDate getHireDay() {
return hireDay;
}
public void setHireDay(int year, int month, int day) {
this.hireDay = LocalDate.of(year,month,day);
}
/**
* 调薪
* @param byPercent
*/
public void raiseSalary(double byPercent){
double raise = salary * byPercent / 100;
salary += raise;
}
}
对于上述代码进行剖析:
1、构造器
- 构造器跟类同名
- 每个类可以有一个以上构造器(方法重载)
- 构造器可以有0、1、多个参数
- 无返回值
- 伴随着new一起调用(new 对象)
2、java10中,可以用car关键字声明局部变量,但参数和字段类型必须声明
3、注意null引用:“严格性”方法拒绝null参数
java9开始,可用Objects.requireNonNull(n,“n can not be null”);
4、隐式参数和显示参数
alan.raiseSalary(5); //alan为隐式,5为显示
5、封装?
6、类的访问权限
7、私有方法
8、final关键字,有final后就没有set方法取修改他,不可变
4、静态字段和静态方法
1、静态字段:
private static int nextId = 1;
此时nextId是共用的,属于真各类,不属于某个对象,为1时,其他对象获取也为1,若是被某个对象修改为2,其他获取到的也是2
2、静态常量
private static final double PI = 3.141586;
直接通过 类名.PI调用,两种使用途径:
3、静态方法
同静态变量,类名.方法名 调用
- 方法不需要访问对象状态,所需参数都从显示参数获得,如Math.pow()
- 访问静态字段,如Employee.getNextId
4、工厂方法
静态方法的第三种用途,通过调用静态方法生成对象
5、方法参数
案例
/**
* @author AlanLiang
*/
public class ParamTest {
public static void main(String[] args) {
/*
Test 1: 方法不能修改基本数据类型得参数
*/
System.out.println("测试tripleValue方法");
double x = 10;
tripleValue(x);
System.out.println(x);
System.out.println();
/*
Test2:方法可以改变对象参数得状态
*/
Employee alan = new Employee("alan", 1000.00);
System.out.println(alan.getSalary());
tripleSalary(alan);
System.out.println(alan.getSalary());
System.out.println();
/*
Test3:方法不能让一个对象参数引用一个新的对象
*/
Employee a = new Employee("a", 100.0);
Employee b = new Employee("b", 200.0);
swap(a,b);
System.out.println(a.getName()+a.getSalary());
System.out.println(b.getName()+b.getSalary());
}
public static void tripleValue(double x) {
x = 3 * x;
System.out.println("x = "+x);
}
public static void tripleSalary(Employee x){
x.raiseSalary(200);
System.out.println("salary = "+x.getSalary());
}
public static void swap(Employee x ,Employee y){
Employee temp = x;
x = y;
y = temp;
System.out.println("x = "+x.getName());
System.out.println("y = "+y.getName());
}
}
class Employee{
private String name;
private double salary;
public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
public void raiseSalary(double percent){
double raise = salary * percent / 100;
salary += raise;
}
}
5、构造对象
1、方法重载
- 刚才第二点已经提及,一个类里可以有多个构造器,而构造方法又是跟类名相同,其实这里就是运用了方法的重载。如果多个方法,拥有相同名字,不同的参数,这便出现了重载(overloading),在编译时,编译器根据参数匹配找到合适的调用方法,这个过程就叫做重载解析。
- 要想完整地描述一个方法,需要指定方法名及参数类型,这叫做方法的签名,但返回类型不是方法的签名的一个部分,也就是说,不能出现两个同名,同参数类型,但是却有不同返回值的方法
2、默认字段初始化
若在构造器中没有显示将字段设为初值,就会自动赋值为默认值,如int为0,boolean为false
3、无参构造器
仅在类没有其他构造器方法时,才能自动生成一个默认的无参构造器。如果自己已经编写了一个带参数的构造方法,即必须也要编写一个无参的构造方法,否则此时调用无参构造方法会报错
4、显示字段初始化
如第2点所说,在类定义时,可直接为字段赋值,初始值也可通过调用方法进行赋值
5、this关键字,this指示隐式参数,即构造器对象
两种使用场景:
public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
public Employee(double salary) {
//代码复用,调用了Employee(String name, double salary)这个构造方法
this("Employee #"+ nextId,salary);
nextId++;
}
6、初始化块
添加static关键字的块是静态初始化块,只会在类第一次加载的时候运行一次;没有static关键字的块是对象初始化块,每次调用构造函数的时候都会运行,其执行顺序在构造器主体代码前
7、java doc p148
8、类设计技巧
-
一定要保证数据即实例字段私有,不要破坏封装性
-
一定要对实例字段进行显示初始化
-
不要在类中使用过多的基本类型,如直接用一个名为Address的新类替换Customer中的street、city、state…
-
不是所有字段都需要get和set方法,好比如构造员工对象,那么她的入职日期就肯定不需要再更改
-
分解过多职责的类,意思是每个类的功能或职责最好尽量单一化,不要过分冗余
-
类名和方法名能一眼看出职责,知道是干嘛的
-
优先使用不可变类----没有方法能修改对象的状态,保证线程安全等名单时有些类也应该可变,比如员工加薪raiseSalary