面向对象
-
java语言属于面向对象的语言
-
面向对象本身不是一门具体的技术,符合人类思维的思想.
-
宗旨:把一个复杂的大型的业务,利用面向对象的思想进行面向对象的分析,
接着面向对象的设计,最终进行面向对象的编程.
-
对象是数据在内存中的”载体”
重新认识类和对象
-
类是抽象的概念,对象是具体的概念
-
类是构建对象的蓝图/模板,对象是类的具体的实例化[表现]
-
类是用来描述对象的.
-
比如:
类:水果 交通工具 学生
对象:榴莲 飞机 小三
比如:
id | username | age |
---|---|---|
100 | jack | 18 |
101 | tom | 17 |
面向对象的思想
核心:”一切皆对象”
①分析出系统中业务的关键对象 - “找对象” - 未来页面上显示的数据
②找出对象的**”属性”** - “对象拥有什么”
③分析出对象的”方法” - “对象可以干什么,对象的功能”
④找出对象与对象之间的关系.
1:1 - 一个丈夫对应一个妻子,一个妻子对应一个丈夫.
1:N - 一个客户拥有多个订单.一个订单只能属于一个客户 - 70%
N:N - 一个学生可以选择多门课程,一门课程可以被多个学生来选择.
自关联 - 自己关联自己.
⑤最后一步才会去考虑具体怎么编码实现.可能也会考虑数据结构或者算法.
“相亲业务” -
- 媒婆帮你找一个”对象”
- 你要查看”对象”的属性
- 询问”对象”的功能
- 分析你和”对象”是否来电
- 考虑 - 追 - 彩礼 - 结婚 - 具体怎么实现!
面向过程的思想
C是典型的面向过程的思想
①优先考虑的就是算法和数据结构 - 考虑的就是具体怎么实现.
实体类
实体类就是用来描述对象的.
实体类中包含属性,构造方法,getter/setter方法,toString方法.
如何定义属性
语法:
修饰符 数据类型 属性名 [=初始值];
修饰符
public 公开的
private 私有的
数据类型
- 八种基本数据类型
- 内置对象类型以及自定义对象类型.
属性名
参考变量的命名 - “小驼峰”
三大特征 - 封装性
封装 - Encapsulation - 屏蔽底层的细节,数据提供保护的机制
-
属性私有化
-
提供getter/setter方法
setter方法 - set属性名[首字母变大写] - 推荐的命名规则
setter方法这种设置值的好处
2-1. setter方法中是可以进行参数有效性判断的.
2-2. 业务需求改变了 - 属性名称改变了,使用到这个属性的具体的地方都要发生改变 - “可维护性”
2-3. 并不是所有的属性的值都是通过那么太简单的计算得到的.
public class Orders{
private int id;
private double price;
private int count;
private double total;
//getter/setter - 提供好了
public double getTotal(){
double result = this.price * this.count;
return result;
}
}
Orders o = new Orders();
o.setPrice(100.0);
o.setCount(10);
//o.setTotal(o.getPrice()*o.getCount());
System.out.println(o.getTotal());
2-4. 并不是所有的属性都需要对外进行公开的.
this关键字
-
作用1 - 当前对象,区分方法中参数和实体类中的定义的属性 - 当两者同名的时候.
-
调用当前实体类中已经存在的另外一个构造方法.
2-1. this()调动空参
2-2. this(参数1,参数2) - 带参构造
2-3. this调用构造的语句一定要在构造体的首行
-
this和super语句的区别
toString方法
- 当我们直接输出对象的时候,默认一定会去调用toString方法 - 如果这个对象是null,那么不会调用toString方法的
- 当自定义的实体类中没有手动提供toString方法,那么这个对象会自动去调用java.lang.Object类中的toString方法
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
-
java.lang.Object - 它是所有的类的根类,基类,超类,父类.
如果某个类没有明确指定它的父类,那么这个类默认会直接继承java.lang.Object类
“User类是Object类的儿子” - 规定 - “子类可以调用父类中所有的公开的成员“
-
Object类中提供的toString方法只能用来返回对象的内存地址 - 不满足实际的打印需求
所以需要在实体类中重新重写这个toString方法.
-
当子类User类一旦重写了toString方法之后,那么就会采取”就近原则”,直接调用自己重写
之后的toString方法.
构造方法-constructor
作用:创建对象的时候调用构造方法,通过new关键字
可以在创建对象的同时进行赋值操作,赋值的方式只有1次,setter赋值,可以多次赋值
特点
-
当实体类中如果没有手动提供任何一个构造方法的时候,那么系统会自动分配一个
空参构造.如果实体类中一旦手动提供了带参构造之后,那么系统将不会再分配一个空参构造了.
-
定义的时候,不需要返回类型的
public 类名([参数列表]){
//构造体
}
- 构造允许重载的
- 实体类中推荐只用提供空参,全参
业务类和实体类合二为一
居然将业务方法放在了实体类中 - 违背了设计原则 - “单一职责原则”.不要给一个类增加太多的负担
实体类 - 主要是描述对象,数据在内存中的载体 - 注重的应该是数据的存储
业务类 - 存放的是业务方法 - 在于操作这些数据
员工类[编号id,姓名name,工资salary]
业务方法[crud增删改查]放在实体类中 - 【不合理的】
public class Emp{
private int id;
private String name;
private double salary;
//一个上司拥有多个下属
//一个下属对应一个上司
//自关联
private Emp[] emps;
public Emp() {
}
public Emp(int id, String name, double salary, Emp[] emps) {
this.id = id;
this.name = name;
this.salary = salary;
this.emps = emps;
}
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 double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public Emp[] getEmps() {
return emps;
}
public void setEmps(Emp[] emps) {
this.emps = emps;
}
//给上司添加一个下属
public void addEmp(Emp e){
if (null!=emps){
this.emps = Arrays.copyOf(emps,emps.length+1);
emps[emps.length-1] = e;
}else {
System.out.println("你是一个员工,你没有资格添加!");
}
}
//输出下属的个数
public void printEmpCount(){
if (null==emps){
System.out.println("你是一个员工,怎么会有下属呢?你这不是扯蛋吗...");
}else {
System.out.println("您下属的个数为:" + emps.length);
}
}
//输出某个上司所有的下属信息
public void printEmp(){
System.out.println("您下属的信息如下:");
if(null!=emps && emps.length>0){
System.out.println("下属信息如下: ");
for (Emp emp : emps) {
System.out.println("\t"+emp);
}
}else{
System.out.println("\tsorry你是一个小员工,没有资格!");
}
}
}
//TestEmp.java
Emp boss = new Emp(1,"boss",100.0,new Emp[0]);
Emp e1 = new Emp(1,"boss",100.0,null);
业务类和实体类分开
业务方法应该专门抽取出来放到一个【业务类】中.
public class Emp {
private int id;
private String name;
private double salary;
private Emp[] emps;
public Emp() {
}
public Emp(int id, String name, double salary, Emp[] emps) {
this.id = id;
this.name = name;
this.salary = salary;
this.emps = emps;
}
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 double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public Emp[] getEmps() {
return emps;
}
public void setEmps(Emp[] emps) {
this.emps = emps;
}
@Override
public String toString() {
return "Emp{" +
"id=" + id +
", name='" + name + '\'' +
", salary=" + salary +
", emps=" + Arrays.toString(emps) +
'}';
}
}
public class EmpBiz {
//将新的员工添加到boos的下属数组中
public void addEmp(Emp boos,Emp e){
Emp[] emps = boos.getEmps();
if (null!=emps){
emps = Arrays.copyOf(emps,emps.length+1);//扩容
emps[emps.length-1] = e;
}else {
System.out.println("你没有资格!");
}
boos.setEmps(emps); // 值 : 地址
}
public void printEmp(Emp boos){
Emp[] emps =boos.getEmps();
if (emps!=null && emps.length>0){
System.out.println("您的下属信息如下:");
for (Emp emp : emps) {
System.out.println(emp);
}
}else {
System.out.println("您是什么勾八,自己是一个员工,心里没点逼数吗?");
}
}
//删除工资高于salary
public void delBySalary(Emp boss,double salary){
Emp[] emps = boss.getEmps();
int count = 0;
for (int i = 0; i < emps.length; i++) { //统计薪资大于salary的数量
if (emps[i].getSalary() > salary){
count++;
}
}
Emp[] temp = new Emp[emps.length-count];
int index = 0;
for (int i = 0; i < emps.length; i++) {
if (emps[i].getSalary() < salary){
temp[index++] = emps[i];
}
}
boss.setEmps(temp);
}
}
继承Inheritance
将多个具有共同特点的对象抽象出一个”父类”.
父类中存储的是各个子类共同的特点[属性]或者功能[方法]
继承的好处 - 提高了代码的复用性.使用多态的前提,为了代码的可拓展性.
继承的缺点 - 增加了类与类之间的关系.不太符合软件的设计原则”高内聚,低耦合”
——————低耦合 - 模块与模块之间/类与类之间的关系应该越弱越好 - 没有关系
——————高内聚 - 单个模块/类能够独立完成某个业务功能的能力比较强.
——————实际开发中,可以采用设计原则**”合成复用原则”** - 来替代继承的使用.
使用extends关键字来表示某个类继承某个父类
比如:
public class A{ } public class B extends A{ } B 是 A的子类,A是B的父类,基类,超类. 1. A的父类就是java.lang.Object - 当某个类如果没有明确指定它的父类是谁,那么这个类默认继承Object 2. 子类是可以访问到父类中所有的非私有的成员 3. java的类只支持单继承.一个类【只能继承一个父类】.避免出现网状结构,保证类的层次性. java中的接口是支持多重继承.一个【接口可以继承多个接口】. 4. 继承具有传播性.C extends B B extends A C间接继承于A,C也是可以访问到A中的所有的非私有的成员 5. 不要滥用继承,前提B is A
super关键字
【必须只能放在构造方法的首行】
语法规则:一个构造方法第一行如果没有this(…);也没有显示的去调用super(…);
系统会默认调用super();
-
调用父类的构造
在子类的构造中,如果没有出现任何的super语句.那么系统默认会给定super(); - 【调用父类的空参构造】
-
当在子类中调用了一个和父类中同名的成员的时候,需要通过super关键字来加以区分[强制调用父类中那个同名的成员]
public class Animal { private String desc; public Animal() { System.out.println("====1===="); this.desc = getDesc(); } public String getDesc() { System.out.println("扰乱你,压根不会执行!"); return "Animal"; } public String toString() { return desc; } } public class Worf extends Animal { private String name; private double weight; public Worf(String name, double weight) { super(); System.out.println("===3==="); this.name = name; this.weight = weight; } public String getDesc() { System.out.println("===2==="); return "Worf:" + name + "体重:" + weight; } public static void main(String[] args) { System.out.println(new Worf("羊", 10)); } //结果:Worf:null体重:0.0 }
public class Super { int j = 10; public Super() { System.out.println("===1=="); print(); j = 20; } public void print() { System.out.println("没有关系"); System.out.println(j); } } public class Sub extends Super { //子类的属性赋值时机等到父类走完!!! int j = 30; public Sub() { super(); System.out.println(j); System.out.println("===3==="); print(); j = 40; } //子类重写了父类中的print方法,所以不会去调用父类 public void print() { System.out.println("===2==4==="); System.out.println(j); } public static void main(String[] args) { System.out.println(new Sub().j); } //结果:0 30 30 40 }
多态 - polymorphism
对象有多种形态.发生前提是具有继承关系
相同的消息可能会送给多个不同的类别之对象,
而系统可依据对象所属类别,引发对应类别的方法,而有不同的行为。简单来说,所谓多态意指相同的消息给予不同的对象会引发不同的动作。
instanceof
进行类型的判断,只有为true的时候,才能够进行类型的转换[强制,向下]
如果没有进行类型的判断,随便进行了一个强制类型转换,可能会抛出java.lang.ClassCastException类型转换失败异常
三种场景应用
面向父类编程
-
对象的编译时类型写成父类,对象的运行时类型写成子类
编译时类型 对象名 = new 运行时类型();
对象的编译时类型决定了对象的访问能力 - “对象名只能访问到编译时类型中定义的成员”
对象的运行时类型决定了对象的行为能力 - “子类中如果一旦重写了父类中的方法,那么运行期间调用的是子类重写之后的方法”
-
方法的参数类型写成父类,调用这个方法的时候,可以传入这个父类的任意一个子类对象
-
方法的返回类型写成父类,方法的返回结果可以是这个父类的任意一个子类对象 - “简单工厂设计模式”
final关键字
不可更改,不可变
- final修饰的变量/属性 - 不能够重新赋值
- final修饰的类 - 不能够被继承
- final修饰的方法不能够被重写.
修饰符
访问修饰符 - 决定了这个成员可以在哪里能够被访问.
所有包 | 子类[可以不同,同包] | 同包 | 本类 | |
---|---|---|---|---|
public 公共的,公开的 | √ | √ | √ | √ |
protected 受保护的 | × | √ | √ | √ |
默认的 | × | × | √ | √ |
private 私有的 | × | × | × | √ |
安全级别:private>默认的>protected>public
可见区域:public>protected>默认的>private
方法的重写
方法的重载和方法的重写有什么区别?
特点:
发生的前提是要有继承关系
- 重写有个特殊的场景 - 子类重写之后的方法的修饰符,返回类型,方法名,参数列表和父类中高度保持一致 - 重构
- 子类重写的方法的访问修饰符可以小于或者等于父类
- 方法名必须要一样.
- 子类重写的方法的返回类型可以小于[可以是父类方法的返回类型的子类类型]或者等于父类的那个方法
- 参数列表必须要高度保持一致