软件的开发方式
面向过程
-
站在过程的角度思考问题,强调的是我该怎么去做。即功能的执行的过程,即先干什么,后干什么
-
面向过程的缺点
- 面向过程,系统软件的适应性差,可拓展性差,维护性低
面向对象
-
面向对象编程,是一种通过对象的方式 把现实世界映射到计算机模型中的一种编程方法.
-
站在对象的角度思考问题,将多个功能合理的放到不同的对象中,强调的是我应该让谁来做。
-
面向对象的三大特征
- 封装(encapsulation)
- 继承(inheritance)
- 多态(polymorphism)
-
面向对象中最小的程序单元是类,必须先存在类的定义,再有对象,从而具备某种功能的实体。
-
在面向对象的世界里。如果你想做什么事情,首先找对象。
面向过程和面向对象的区别?
-
面向过程是站在过程的角度上思考,注重的是实现方法的步骤,过程。
面向对象是站在对象的角度上思考的,注重的是强调做某件事的个体、对象。
-
过程是由一个个步骤组成的,然后将这些步骤转换成为函数。
对象是由字段(成员变量、实例变量)和方法(成员方法)组成的。
-
面向过程:性能较好,常常使用在单片机、嵌入式开发 可是 系统软件的适应性差,可拓展性差、维护性低
面向对象:易于维护、易于复用,易于扩展,由于面向对象有封装、继承、多态的特性,可以设计出低耦合的系统,使系统更加灵活,更加易于维护。
什么是抽象
- 所谓抽象,从特定的角度出发,从已经存在的多个事物中抽取我们所关注的特性、行为、从而产生一个新的事物的思维过程,是一种从复杂到简洁的思维方式。
- 还可以这样去理解:所谓抽象,就是将现实世界的事物,提取公共的特征和行为,也即提取其像的部分,提取到计算机中的过程,假设我们要做一个宠物管理系统,那么我们需要对宠物进行抽象 -> 提取宠物的公共特征和行为-> 提取到计算机中,-> 计算机怎么描述这个抽象(定义一个类)->通过类创建对象。
成员变量和局部变量
变量的分类
-
根据变量定义的位置的不同
-
成员变量:直接定义在类中,方法外名。又称之为字段,不要称之为属性。
-
局部变量:除了成员变量,其他都是局部变量 ,具体在三个地方
- 方法内
- 方法的形参
- 代码块中(一对花括号)
-
Demo01Var.java
public class Demo01Var { // 成员变量(字段):在类的内部 在方法的外部 作为类的成员 不能叫属性 int count = 10; public static void add(int num1,int num2){ // 局部变量 方法里面定义的变量 形参 int r = num1 + num2; // 代码块 用{}起来的内容成为代码块 可以形成作用域 { int count = 20; System.out.println(count); System.out.println(r); } // System.out.println(count); /** * 总结: * 1. {}成为代码块 可以形成作用域 * 2. 作用域可以嵌套 内层作用域可以访问外层的作用域的变量,外层的作用域不能访问内层的作用域的变量 */ } public static void main(String[] args) { add(10,20); } }
-
变量从初始值
- 成员变量:默认是有初始值的,不同类型的初始值
- 局部变量:没有初始值,需要先声明,然后初始化,才能使用
变量的作用域
- 成员变量:在所有定义的类中都有效
- 局部变量:从开始定义的位置开始,只能在自己所在的花括号内有效
变量的生命周期
- 成员变量:存储在堆内存中,随着对象的销毁而销毁 (main方法结束)
- 局部变量:存储在栈内存中,随着所定义的方法的调用结束而销毁。
- 局部变量存储在方法中,每次调用方法,都会在在空间开辟一块内存空间 – 栈帧,方法调用结束,栈帧被销毁,内存中的存储的变量数据也销毁了。
类和对象的关系
- 类:是对某一类事物的抽象描述(状态和行为)
- 类是一组具有相同特征和行为的对象的抽象描述(集合),类是一个群体概念。
- 对象:表示对现实生活该类事物的个体,也称之为实例。
- 对象是类这个群体中的一个唯一的、独立的个体,对象是个体概念,这个个体在程序中成为实体或者实例。
- 类可以看作是对象的数据类型。
类的定义
- 使用成员变量(字段、实例变量)来表示状态。
- 使用成员方法(方法)来表示行为。
public class 类名{ //可编写0到N个成员变量
[修饰符] 数据类型 变量名1;
[修饰符] 数据类型 变量名2;
//可编写0到N个成员方法
[修饰符] 返回值类型 方法名称(参数){
//方法体
}
}
在描述对象的类中,不需要定义main方法,专门在测试的类中定义main方法。
在面向对象设计中,描述对象的类和测试类分开来编写。
对象的操作
基本操作
- 创建对象
类名 对象变量名 = new 类名();
- 通过对象调用字段
对象名.字段名 = 值;
- 获取字段的值
数据类型 变量名 = 对象名.字段名;
- 对象调用方法
对象变量名.方法(参数);
-
定义一个Student类
public class Student { String sid; String name; int age; /** * 成员方法 => 类的行为 * 一个成员方法 * @param aClass */ public void learn(String aClass) { System.out.println(name + "正在学习" + aClass); } public void sayHi() { System.out.println(name + "同学,正在Say Hi."); } }
-
调用Student类
public class Demo01Test { public static void main(String[] args) { // 1> 类是一种数据类型 // [1] 可以用于声明变量 Student s1 = null; // [2] 为变量s1申请堆内存空降 s1 = new Student(); // 合二为一 // new了一个s1对象,s1也称之为student的实例,又称为student的一个对象 Student s2 = new Student(); System.out.println(s2); Student s3 = new Student(); System.out.println(s3); // s1 ,s2都属于Student类的对象,但是他们这个是独立的且是唯一的 // s1,s2 都属于同一类(Student) s1和s2都具有Student中描述的特性(状态)和行为(功能) s1.age = 10; s1.name = "小明"; s1.learn("Java"); } }
构造器
- 构造器的语法
// 无参构造方法
[修饰符] 类名(){
}
// 有参构造方法
[修饰符] 类名(参数){
字段1 = 参数1;
}
注意:
构造器名字和类的名字要高度一致
构造器有两种创建的方法,一个是有参数的构造器,一个是无参数的构造器,这两种方法构成了重载
不能定义返回值类型
不能使用return语句
public class User {
String uid;
String name;
int age;
String info;
/**
* 无参构造方法一定用于对实例进行赋初始值/默认值
*/
public User() {
age = 18;
info = "这个人很懒,什么也没有留下";
}
/**
* 如果想在构建对象的时候就给对象赋值,此时我们可以考虑使用有参构造
*/
public User(String aUid, String aName, int Age, String aInfo) {
uid = aUid;
name = aName;
age = Age;
info = aInfo;
}
/**
* 如果一个类,开发者没有显示的定义任何构造方法,jvm会默认分配无参构造
* 如果开发者定义了有参或者无参,jvm都不会再分配无参构造
* 好的开发者;当定义了有参构造的时候,一定习惯的把无参构造给加上,哪怕无参构造什么也不做
*/
public void showInfo() {
System.out.println("姓名:\t" + name);
System.out.println("年龄:\t" + age);
System.out.println("编号:\t" + uid);
System.out.println("简介:\t" + info);
}
}
构造方法
- 创建实例的时候,我们经常需要同时初始化这个实例的字段
Person ming = new Person();
ming.setName("小明");
ming.setAge(12);
初始化对象实例需要3行,而且,如果忘记调用setName() 或者 setAge(),这个实例的内部的状态就是不正确的.
-
在创建对象实例的时候就把内部字段全部初始化为合适的值?
-
这时 , 我们需要构造方法
-
创建实例的时候,实际上是通过构造方法来初始化实例的,我们先定义一个构造方法,能在创建Person实例的时候,一次性传入name和age,完成初始化:
class Person{
private String name;
private int age;
public Person(String name,int age){
this.name = name;
this.age = age;
}
public String getName(){
return this.name;
}
public int getAge(){
return this.age;
}
}
- 构造方法没有返回值(和普通方法相比) 调用构造方法,必须使用new操作符.
- 构造方法的参数没有限制,在方法内部,也可以编写任意语句
- 构造方法的名字就是类名
默认构造方法
- 一个类如果没有定义任何构造方法,编译器会自动帮我们生成一个默认构造方法,他没有参数,也没有执行语句
class Person{
public Person(){
}
}
- 如果我们自定义了一个构造方法,那么,编译器就不再自动创建默认的构造方法了
构造器总结
构造器(或构造方法、constructor)的使用
- construct:建设、建造。 construction:CCB constructor:建设者
一、构造器的作用:
- 1.创建对象
- 2.初始化对象的信息
二、说明:
- 1.如果没有显式的定义类的构造器的话,则系统默认提供一个空参的构造器
- 2.定义构造器的格式:权限修饰符 类名(形参列表){}
- 3.一个类中定义的多个构造器,彼此构成重载
- 4.一旦我们显式的定义了类的构造器之后,系统就不再提供默认的空参构造器
- 5.一个类中,至少会有一个构造器。
练习
请给Person类增加(String,int)的构造方法
class Person{
private String name;
private int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
}
为什么存在对象
- 为什么存在对象?
- 对象是数据的载体,开发本质上就是把数据组存入计算机中,需要一种更加复杂的数据类型,而且这个复杂的数据类型一定要是自定义的
- 进而,软件开发就是把现实中的数据存储到计算机中,必须把共同的特性和行为抽象到计算机中作为类而存在,类又可以创建对象
- 软件开发数据的展示是,一定要找对象 => 无论你做什么都需要找对象 => 在你眼里一切皆需要对象 => 你眼里只有对象 => 这就是面向对象 => 面向什么就可以看到什么?
封装思想
- 一个class可以包含多个字段(field),例如我们给person类定义两个field
class Person{
public String name;
public int age;
}
- 但是,直接把field用public暴露给外部,可能会破坏封装性,比如
Person p = new Person();
p.name = "小明";
p.age = -99;
- 直接操作field 容易造成逻辑混乱. 为了避免外部代码直接去访问field,我们可以用private修饰field,拒绝外部访问:
class Person{
private String name;
private int age;
}
- 封装是面向对象的三大特征之一,
- 将对象和字段存放在一个独立的模块中(类)
- 隐藏信息,尽可能隐藏对象的数据和功能的实现细节
- 好处
- 保证数据的安全性,防止调用者随意修改数据
- 提高组件的重用性,把公用功能放到一个类中,谁需要该功能,直接调用即可。
private方法
- private方法不允许外部进行调用
- 定义private方法的理由是内部方法可以调用private方法的
class Person{
private String name;
private int birth;
public void setBirth(int birth){
this.birth = birth;
}
public int getAge(){
// 调用private方法
return calcAge(2019);
}
private int calcAge(int currentYear){
return currentYeat - this.birth;
}
}
- 上述代码中 calaAge()是一个private方法,外部代码无法调用,但是,内部方法**getAge()**可以进行调用
- 这个Person类只是定义了birth字段,没有定义age字段,获取age的时候,通过方法getAge()返回的是一个实时计算的值,并非存储在某个字段中的值,这说明方法可以封装一个类的对外的接口,调用方不需要知道,关心person实例在内部到底有没有age字段.
this变量
- 在方法内部,可以使用一个隐含的变量this,它始终指向当前的实例.因此,通过this.field就可以访问当前实例的字段,如果没有命名冲突,可以省略this
class Person{
private String name;
public String getName(){
return name; // 相当于this.name
}
}
- 但是,如果有局部变量和字段重名,那么局部变量的优先级更高,就必须加上this:
class Person{
private String name;
public void setName(String name){
// 前面的this必不可少,少了就变成局部变量name了
this.name = name;
}
}
封装总结
封装与隐藏: 是面向对象的特征之一
一、问题的引入:
当我们创建一个类的对象以后,我们可以通过"对象.属性"的方式,对对象的属性进行赋值。这里,赋值操作要受到属性的数据类型和存储范围的制约。除此之外,没有其他制约条件。但是,在实际问题中,我们往往需要给属性赋值加入额外的限制条件。这个条件就不能在属性声明时体现,我们只能通过方法进行限制条件的添加。(比如:setLegs())同时,我们需要避免用户再使用"对象.属性"的方式对属性进行赋值。则需要将属性声明为私有的(private).
- –>此时,针对于属性就体现了封装性。
二、封装性的体现:
- 我们将类的属性xxx私有化(private),同时,提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值
- 拓展:封装性的体现:① 如上 ② 不对外暴露的私有的方法 ③ 单例模式 …
三、封装性的体现,需要权限修饰符来配合。
- 1.Java规定的4种权限(从小到大排列):private、缺省、protected 、public
- 2.4种权限可以用来修饰类及类的内部结构:属性、方法、构造器、内部类
- 3.具体的,4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类
-
修饰类的话,只能使用:缺省、public
- 总结封装性:Java提供了4种权限修饰符来修饰类及类的内部结构,体现类及类的内部结构在被调用时的可见性的大小。
JavaBean
- JavaBean是一种某些符合条件的特殊的类,但是必须遵守一定的规范
- 类必须使用public修饰 – 类是公共的
- 必须保证有公共无参数构造器,即使手动提供了带参数的构造器,也带手动提供无参数的构造器
- 字段使用private修饰(私有化),每个字段提供一对getter和setter方法
public class User {
private String name;
private String uid;
private int age;
public User() {
}
public User(String name, String uid, int age) {
this.name = name;
this.uid = uid;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}