软件设计全解析:从生命周期到面向对象编程的三大特征
一、软件设计的概况

1. 软件的生命周期
2. 软件设计原则
二、面向过程
也需要处理方法的调用。
存在的问题是:采用从上往下进行设计的方式,只是分别去考虑每个方法要做什么,这样在进行细分的时候就会比较困难。
需求:做饭(买菜,做饭,做菜,吃饭,洗碗)
三、面向对象
面向对象:OOP(Object Oriented Programming),从对象的视角来思考问题,处理类与类之间的调用及操作,将每一个类拆分成不同的功能。
2. 面向对象三大特征:
3.类:具有相同特征和行为的事物进行一个抽象。
4.对象:是类的实例,类是对象的模板。
5.面向对象和面向过程的区别:
四、面向对象特征之封装性 1. 为什么要引入封装性?
我们程序设计追求“高内聚,低耦合”
隐藏对象内部的复杂性,仅将简单的接口对外公开。这样便于外界进行调用,进而能够提高系统的可扩展性与可维护性。通俗来讲,就是把需要隐藏的部分隐藏起来,而把应该暴露的部分暴露出来。这便是封装性的设计思想。
什么是封装呢?封装有其特定的含义和作用。封装的好处和意义体现在多个方面。在 Java 中,规定了四种权限修饰符。
通过设置不同的方法属性的权限修饰符,以实现对类进行封装的目的。
修饰符
类内部
同一个包
不同包的子类
同一个工程
private
Yes
(缺省)
Yes
Yes
protected
Yes
Yes
Yes
public
Yes
Yes
Yes
Yes
权限修饰符可用来修饰的结构说明:
5. 封装的实现
5.1 规范
需要构建一个学生类,其中包含 name、age 和 address 这些属性,并且要提供相应的 set 方法和 get 方法。
public class Student {
private String name;
private int age;
private String address;
public Student(){
}
public Student(String name){
this.name = name;
}
定义一个方法 setName,该方法接收一个字符串类型的参数 name,并将其设置为对象的名称。
this.name = name;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age;
}
设置地址的方法是,传入一个字符串类型的地址参数。 这个方法用于设定对象的地址信息。 它接收一个名为 address 的字符串作为输入。 该字符串将被存储为对象的地址属性。 这样就可以通过这个方法来改变对象的地址。
System.out.println(this);
this.address = address;
}
public String getAddress(){
return address;
}
}
5.2 对于私有属性传参
5.3 注意:
五、面向对象特征之继承性 1. 引入继承性的好处
2. 继承性的思想:
定义:以某个父类为基础进行拓展,从而定义出新的子类。子类能够继承父类原本的属性和行为,同时还可以增添父类所没有的特性,亦或是覆盖父类中的某些特性。
继承关系:is a 用是造句
继承的基础语法:
使用 extends 关键字
公共类子类类名继承自父类类名。
写自己的特征
}
注意:
创建对象时,构造方法如何执行
3. 重写
当父类的特征无法满足子类的特征时,能够对父类的方法进行重新编写。
3.2 要求:
3.3 重写和重载的区别
5. Object类
5.1 java.lang.Object类的理解
Java 中所有类的父类,就如同二叉树里的根节点一样,它定义了一些通用的方法。
如果我们未显式声明一个类的父类,那么此类会继承于 java.lang.Object 类。因为除 java.lang.Object 类之外的所有 java 类都直接或间接继承于 java.lang.Object 类,这也就意味着所有的 java 类都具有 java.lang.Object 类所声明的功能。java.lang.Object类中定义的一些方法
方法名
类型
描述
public Object()
构造方法
构造器
判断此对象是否与指定对象相等,即判断此对象与指定对象的内容是否完全相同,若相同则返回 true,否则返回 false。
普通方法
对象比较
public int hashCode()
普通方法
获取Hash码
public String toString()
普通方法
对象打印时调用
5.2. java.lang.Object类的说明:
Object类是所Java类的根父类
如果在类的声明里没有使用 extends 关键字来指明其父类,那么就默认其父类是 java.lang.Object 类。
Object类中的功能(属性、方法)就具通用性。
Object 类仅声明了一个空参的构造器。数组也可作为 Object 类的子类出现。并且能够调用 Object 类中所声明的方法。
5.3. equals()方法
如果传入的对象为 null,直接返回 false。如果传入的对象不是当前类的实例,也返回 false。只有当传入的对象是当前类的实例且属性值都相同时,才返回 true。
return (this == obj);
}
使用说明:
手动重写举例:
class User{
String name;
int age;
//重写其equals()方法
如果 obj 为 null 或者 obj 的类与当前类不同,那么返回 false;如果 obj 与当前对象是同一个对象,那么返回 true;如果 obj 的类是当前类,并且可以进行类型转换,那么进行字段比较,若所有字段都相等则返回 true,否则返回 false。
如果 obj 等于 this,那么就判断形参和比较对象的引用地址是否相同,如果相同就直接返回 true。
return true;
}
if(obj instanceof User){
User u = (User)obj;
判断当前对象的年龄是否等于传入对象的年龄,并且当前对象的名字是否等于传入对象的名字。
}
return false;
}
}
自动生成的。自动生成的快捷键是 ctrl+alt+S 。可以选择自动生成 hashCode 和 equals 方法。
自动生成的代码:
@Override
如果都相等,返回 true;否则,返回 false。
if (this == obj)
return true;
if (obj == null)
return false;
如果当前类与目标对象的类不相同。
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
如果 name 不等于 other.name ,那么就执行以下操作;如果 name 与 other.name 不相等,就进行后续处理;若 name 和 other.name 不相同,就执行相应的逻辑。
return false;
return true;
回顾 ==运算符的使用:
== :运算符
可以使用在基本数据类型变量和引用数据类型变量中
如果比较的是基本数据类型变量:
比较两个变量保存的数据是否相等。(不一定类型要相同)
如果比较的是引用数据类型变量:
比较两个对象的地址值是否一样,也就是两个引用是否指向同一个对象实体。
使用说明
5.4. toString()方法
toString()的使用:
当我们输出一个对象的引用时,这实际上是在调用当前对象的 toString()方法。
Object类中toString()的定义:
public String toString() {
获取类的名称并加上“@”以及该类的哈希码的十六进制表示形式。即先获取类的名称,然后加上“@”符号,最后将哈希码转换为十六进制字符串并添加在后面。
}
String 重写了 Object 类中的 toString()方法,Date 也重写了,File 同样重写了,包装类也重写了。这样在调用这些对象的 toString()时,就能返回“实体内容”信息。
自定义类能够重写 toString()方法,在调用该方法时,会返回对象的“实体内容”。
重写toString()
//自动实现
@Override
public String toString() {
返回“客户[姓名=" + name + ",年龄=" + age + "]”;
}
6. 子类对象实例化全过程
1. 从结果上看:继承性
子类继承父类之后,能够获取到父类中所声明的属性或方法。当创建子类的对象时,在堆空间里,就会加载父类中声明的属性。
2. 从过程上看:
当我们用子类的构造器去创建子类对象的时候,我们会直接或者间接调用其父类的构造器,接着会调用父类的父类的构造器,如此这般一直到调用了 java.lang.Object 类中无参的构造器为止。正是因为加载了所有的父类的结构,所以我们才能够在内存中看到父类中的结构,子类对象才能够考虑进行调用。
3. 强调说明:
创建子类对象时会调用父类的构造器,然而自始至终只创建过一个对象,这个对象就是通过 new 创建的子类对象。
三、面向对象的特征之多态性 1. 多态性的理解:
可以理解为某一事物存在着多种不同的形态。例如数据库的连接方式,我们明确地定义了数据库的连接,同时也规定了连接时需要遵循的步骤。然而,我们并不能预知用户会使用何种数据库。在没有多态性之前,我们只能针对不同的数据库分别编写不同的连接方法。而当有了多态性之后,我们只需定义好数据库的类,并编写好连接方法,让所有的数据库都继承该数据库类,然后重写数据库连接方法即可。
这样在调用时,我们只需采取声明数据库类且让其指向数据库的子类这种方式,也就是让数据库类的引用指向继承了数据库类的具体实现类的对象,就能够进行数据库连接。而并非要针对不同的数据库去书写不同的连接方式。
2. 何为多态性:
父类的引用能够指向子类的对象,同时子类的对象也可以赋给父类的引用,这体现了对象的多态性。
编译时和运行时类型不一致,产生了多态
代码举例:
Person p = new Man();
Object obj = new Date();
3. 多态性的使用:虚拟方法调用
3.1多态性的使用前提:
① 类的继承关系 ② 方法的重写
3.2多态性的应用举例:
举例一:
公共的 void 方法 func,它接收一个 Animal 类型的参数 animal,即 Animal animal 被初始化为一个 Dog 实例。
animal.eat();
animal.shout();
}
举例二:
此方法用于执行特定的操作或功能。
}
举例三:
class Driver{
公共的 void 方法 doData(Connection conn)。这里的 conn 可以是 new MySQlConnection(),也可以是 new OracleConnection()。
//规范的步骤去操作数据
// conn.method1();
// conn.method2();
// conn.method3();
}
}
3.3 多态性使用的注意点:
3.4 多态使用总结
成员变量:不具备多态性,只看引用变量所声明的类
4. 关于向上转型与向下转型:
向上转型:多态
4.1 为什么使用向下转型:
有了对象的多态性之后,内存里实际上加载了子类特有的那些属性和方法。然而,因为变量被声明为父类类型,所以在编译的时候,只能够调用父类中所声明的属性和方法。而子类特有的那些属性和方法是无法被调用的。那么怎样才能调用子类特有的属性和方法呢?答案是使用向下转型。
4.2 如何实现向下转型:
使用强制类型转换符:()
Person p = new Man();
Man m1=(Man)p2;//向下转型
4.3 使用时的注意点:
使用强转的时候,有可能会出现 ClassCastException 这种异常。
为避免向下转型时出现 ClassCastException 异常,我们需在向下转型前先进行 instanceof 判断。若返回 true ,则进行向下转型;若返回 false ,则不进行向下转型。
只有对象 A 属于 B 的子类且已实例化,或者对象 A 在 B 的下层子类中,才能够进行向下转型。
4.4 instanceof的使用:
a instanceof A 意思是判断对象 a 是否为类 A 的实例。若 a 是类 A 的实例,就返回 true;若 a 不是类 A 的实例,就返回 false。
如果 a 属于类 A 这个情况返回 true,那么 a 属于类 B 也返回 true。并且,类 B 是类 A 的父类。
要求 a 所属的类与类 A 需为子类和父类的关系,不然就会出现编译错误。
例如,如果有一个变量名为 Man,它的类型是类。
5. 面试题:
1.谈谈你对多态性的理解?
实现代码的通用性。举例:
**2.多态是编译时行为还是运行时行为?**运行时行为
class Base {
int count = 10;
public void display() {
打印这个对象的 count 变量。
}
}
class Sub extends Base {
int count = 20;
public void display() {
System.out.println(this.count);
}
}
类名为 FieldMethodTest 的类。它是一个 Java 类。在这个类中包含了字段和方法的相关测试代码。这些测试代码用于验证字段和方法的功能和行为是否符合预期。通过编写这些测试代码,可以确保类中的字段和方法能够正确地工作。并且在代码的修改或扩展过程中,能够及时发现潜在的问题。从而提高代码的质量和稳定性。
公共静态 void 主方法(字符串数组 args) { }
Sub s = new Sub();
System.out.println 输出 s 的 count 值;s 的 count 值为 20 。
s.display();//20
Base b = s;//多态性
对于引用数据类型而言,比较的是两个引用数据类型变量的地址值是否一样。
System.out.println 输出 b 等于 s 的结果;结果为 true
System.out.println(b 中的 count);//10
b.display();//20
}
}