这一堂课听的最多的一句话就是「这部分先了解就可以,后面到了xxx的时候会深入的讲」,就给人一种后面还有茫茫多的只是要学习一样。。。。
不过今天学的这些东西自己倒是能听懂,唯独就是后面的练习题,在各种点来点去的时候,会搞不清楚测试的顺序,创建类什么的都已经ok了,但是就是在使用调用的时候会受忙脚乱,不知道该怎么调用,或者说不知道该调用谁再调用谁,这个估计后面还得多练练才能搞清楚,脑子慢真心伤不起
在就是免费课确实没有付费课香,这么多的资料全都得自己手动敲下来,而不是像人家一样直接点开文件夹拿出报名时候给到的压缩包就好,这个还是比较不爽的。但是这段时间敲英文字的水平确实是有进步,打字快了好多,而且确实还挺快乐的。但是学会了eclipse里面的快速创建构造器、setter和getter的快捷方式的时候,瞬间感觉打字不香了。
🇺🇳 封装性——"我要用洗衣机,只需要按一下开关和洗涤模式就可以了,有必要了解洗衣机内部的结构么?有必要碰电动机么?"
一、我们程序涉及追求"高内聚,低耦合"
> 高内聚:类的内部数据操作细节自己完成,不允许外部干涉
> 低耦合:仅对外暴露少量的方法用于使用
二、隐藏对象内部的复杂性,只对外公开简单的接口,便于外界调用,从而提高系统的可扩展性、可维护性。
通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想
三、面向对象的特征一:封装与隐藏
1、问题的引入
> 当我们创建一个类的对象以后,我们可以通过"对象.属性"的方式,对对象的属性进行赋值。
这里,赋值操作要受到属性的数据类型和存储范围的制约,除此之外,没有其他制约条件
但是,在实际问题中,我们往往需要给属性赋值加入额外的限制条件,这个限制条件就不能在属性声明时体现,
只能通过方法进行限制条件的添加(比如下面写的setLegs())
同时,我们需要避免用户再使用"对象.属性"的方式对属性进行赋值,则需要将属性声明为私有的(private)
此时,针对属性就体现了封装性
public class AnimalTest{
public static void main(String[] args){
Animal test = new Animal();
test.name = "大黄";
test.setLegs(6);
test.show();
}
}
class Animal{
String name;
private int age;
private int legs;
//对于属性的设置
public void setLegs(int l){
if(l >= 0 && l % 2 ==0){
legs = l;
}else{
legs = 0;
}
}
//对于属性的获取
public int getLegs(){
return legs;
}
public void eat(){
System.out.println("动物进食");
}
public void show(){
System.out.println("name = " + name + ",age = " + age + ",legs = " + legs);
}
}
> 属性赋值的先后顺序
> 默认初始化值
> 显式初始化
> 构造器中初始化
⬆️上面三个操作只执行一次
> 通过"对象.set方法"或者"对象.属性"的方式进行赋值
⬆️这条操作是可以反复执行的
以上操作的先后顺序: 1 - 2 - 3 - 4
public class UserTest{
public static void main(String[] args){
User u = new User();
System.out.println(u.age);
User u1 = new User(2);
u1.setAge(3);
System.out.println(u1.age);
}
}
class User{
String name;
int age;
public User(){
}
public User(int a){
age = a;
}
public void setAge(int a){
age = a;
}
}
2、封装性的体现:
> 我们将类的属性私有化(private),同时,提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值
//【练习】
//创建程序,在其中定义两个类:Person和PersonTest类。定义如下:
//用setAge()设置人的合法年龄(0-130),用getAge()返回人的年龄。
//在PersonTest类种实力话Person类的对象b,调用setAge()和getAge()方法,体会Java的封装性
//Person
//-age:int
//+setAge(i:int)
//+getAge();int
public class PersonTest{
public static void main(String[] args){
Person b = new Person();
b.setAge(5);
b.getAge();
}
}
class Person{
private int age;
public void setAge(int i){
age = i;
}
public int getAge(){
return age;
}
}
3、拓展——封装性的体现:
> 如上
> 不对外暴露的私有的方法
> 单例模式(构造器部分讲解)
> ...
4、封装性的体现,需要权限修饰符来进行配合
> Java规定的4种权限(从小到大排列):private、缺省(英文会写作default,但是其实写上就报错了,所以缺省意味着什么都不用写)、protected、public
置于类的成员定义前,用来限定对象对该类成员的访问权限
> 全部4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类
> 只有2种权限可以用来修饰类及类的内部结构:属性、方法、构造器、内部类
> 对于class的权限修饰只可以用public和default(缺省)
> public类可以在任意地方被访问
> default类只可以被同一个包内部的类访问
> 总结封装性:Java提供了4种权限修饰符来修饰类及类的内部结构,体现类及类的内部结构在被调用时的可见性的大小
🇺🇳 构造器
类的结构之三:构造器(或构造方法:constructor,虽然也可以叫做构造方法,但是不能错误的认为构造器就是特殊的方法,格式跟方法不完全一样是一方面,最主要的一方面是功能不一样)的使用——任何一个类都有构造器
类的构成(属性「Field」、方法「Method」、构造器「Constructor」)
一、构造器的作用
> 创建对象
> 初始化对象的信息
二、说明
> 如果没有显示的定义类的构造器的话,则系统默认提供一个空参的构造器 ==> 权限与类相同
> 定义构造器的格式:权限修饰符 类名(形参列表){}
> 一个类中定义的多个构造器,彼此构成重载
> 一旦我们显示的定义了类的构造器之后,系统就不再提供默认的空参构造器了
> 举例:家庭没有劳动力的时候,国家会给扶贫补贴,但是一旦家庭中有一个劳动力了,则国家不给扶贫补贴
> 一个类中,至少要有一个构造器:可能是系统提供的空参构造器,也可能是自己定义的构造器
public class PeopleTest{
public static void main(String[] args){
}
}
class People{
//属性
String name;
int age;
//构造器
public People(){
System.out.println("People()......");
}
public People(String n){
name = n;
}
public People(String n, int a){
name = n;
age = a;
}
//方法
public void eat(){
System.out.println("人吃饭");
}
public void study(){
System.out.println("人可以学习");
}
}
//【练习】
//编写两个类,TriAngle和TriAngleTest,其中,TriAngle类中声明私有的底边长base和高height
//同时声明公共方法访问私有变量
//此外,提供类必要的构造器
//另一个类中使用这些公共方法,计算三角形的面积
public class TriAngleTest{
public static void main(String[] args){
TriAngle test = new TriAngle();
test.setBase(2.0);
test.setHeight(2.4);
System.out.println("base : " + test.getBase() + ",height : " + test.getHeight());
TriAngle test2 = new TriAngle();
System.out.println("base : " + test2.getBase() + ",height : " + test2.getHeight());
}
}
class TriAngle{
private double base;
private double height;
public TriAngle(){
}
public TriAngle(double a,double b){
base = a;
height = b;
}
public void setBase(double b){
base = b;
}
public void setHeight(double h){
height = h;
}
public double getBase(){
return base;
}
public double getHeight(){
return height;
}
public double findArea(){
return base * height * 0.5;
}
}
🇺🇳 this关键字的使用
> 在Java中,this关键字比较难理解,它的作用和其词义很接近
> 它在方法内部使用,即这个方法所属对象的引用
> 可以使用"this.属性"或者"this.方法"的方式,调用当前对象属性或方法,但是通常情况下,选择省略"this."
> 特殊情况下,如果方法的形参和类的属性同名时,必须显式的使用"this.变量"的方式表明此变量是属性,而非形参
> 它在构造器内部使用,表示该构造器正在初始化的对象
> 可以使用"this.属性"或者"this.方法"的方式,调用当前正在创建的对象属性或方法,但是通常情况下,选择省略"this."
> 特殊情况下,如果构造器的形参和类的属性同名时,必须显式的使用"this.变量"的方式表明此变量是属性,而非形参
> this表示当前对象,可以调用类的属性、方法和构造器
> this理解为:当前对象
> this调用构造器
> 我们在类的构造器中,可以显式的使用"this(形参列表)"的方式,调用本类中指定的其他构造器。
而且不管调用了多少个本类的其他构造器,其实结果就是只创建了一个对象
> 构造器中不能通过"this(形参列表)"的方式调用自己
> 如果一个类中有n个构造器,则最多有n-1个构造器中使用了"this(形参列表)"的方式
> 【规定】"this(形参列表)"的方式调用其他构造器,必须声明在当前构造器的首行,如果不是首行,就会报错
「Constructor call must be the first statement in a constructor」
> 构造器内部,最多只能声明一个"this(形参列表)"的方式,用来调用其他的构造器
> 什么时候使用this关键字?
> 当在方法内需要用到调用该方法的对象时,就用this
具体的:我们课用this来区分局部变量和属性
例如:this.name= name;
public class ThisTest{
public static void main(String[] args){
Person p1 = new Person();
p1.setAge(1);
System.out.println(p1.getAge());
p1.eat();
Person p2 = new Person("Jerry",20);
System.out.println(p2.getAge());
}
}
class Person{
private String name;
private int age;
public Person(){
String info = "此处省略40条代码";
System.out.println(info);
}
public Person(String name){
this();//表明调用上方的空参构造器
this.name = name;
}
public Person(int age){
this();
this.age = age;
}
public Person(String name, int age){
this(age);//表示调用上面这个(int age)的构造器,此时,本构造器中的age会直接赋值给上一个构造器的形参,所以下面的"this.age = age"代码其实就可以删掉了
this.name = name;
this.age = age;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age;
}
public void eat(){
System.out.println("人吃饭");
study();//其实前面省略了一个"this."
}
public void study(){
System.out.println("人学习");
}
}
⚠️ JavaBean是一种Java语言写成的可重用组件
JavaBean是符合以下标准的Java类
> 类是公共的
> 有一个无参的公共的构造器
> 有属性,且有对应的get,set方法
用户可以使用JavaBean将功能、处理、值、数据库访问和其他任何可以用Java代码创造的对象进行打包
并且其他的开发者可以通过内部的JSP页面、servlet、其他的JavaBean、applet程序或者应用来使用这些对象
用户可以认为JavaBean提供来一种随时随地的复制和粘贴的功能,而不用关心任何改变
两道练习题
//> 写一个名为Account的类模拟账户。该类的属性和方法如下所示。
// > 该类包括属性:
// > 账号id,余额balance,年利率annualInterestRate
// > 该类包括方法:
// > 访问器方法(getter和setter方法),取款方法withdraw(),存款方法deposit()
// Account
// private int id
// private double balance
// private double annualInterestRate
// public Account(int id,double balance,double annualInterestRate)
// public int getId()
// public double getBalance()
// public double getAnnualInterestRate()
// public void setId(int id)
// public void setBalance(double balance)
// public void setAnnualInterestRate(double annualInterestRate)
// public void withdraw(double amount)//取钱
// public void deposit(double amount)//存钱
// 提示:在提款方法withdraw中,需要判断用户余额是否能够满足提款数额的要求,如果不能,应该出提示
// > 创建Customer类
// Customer
// private String firstName
// private String lastName
// private Account account
// public Customer(String f,String l)
// public String getFirstName()
// public String getLastName()
// public Account getAccount()
// public void setAccount(Account account)
// a.声明三个私有对象属性:firstName、lastName和account
// b.声明一个公有构造器,这个构造器带有两个代表对象属性的参数(f和l)
// c.声明两个公有存取器来访问该对象属性,方法getFirstName和getLastName返回相应的属性
// d.声明setAccount方法来对account属性赋值
// e.声明getAccount方法来获取account属性
// > 写一个测试程序
// > 创建一个Customer,名字叫Jane Smith,他有个账号为1000,余额为2000元,年利率为1.23%的账户
// > 对Jane Smith操作
// > 存入100元,再取出960元,再取出2000元
// > 打印出Jane Smith对基本信息
public class AccountCustomer{
public static void main(String[] args){
Customer c = new Customer("Jane","Smith");
Account a = new Account(1000,2000,0.0123);
c.getAccount().deposit(100);
c.getAccount().withdraw(960);
c.getAccount().withdraw(2000);
System.out.println("Customer [" + cust.getLastName() + "," + cust.getFirstName() + "] has an account: id is " + cust.getAccount().getId() + ", annualInterestRate is " + cust.getAccount().getAnnualInterestRate() * 100 + "%, balance is " + cust.getAccount().getBalance());
}
}
class Account{
private int id;
private double balance;
private double annualInterestRate;
public Account(int id, double balance, double annualInterestRate){
}
public int getId(){
return id;
}
public double getBalance(){
return balance;
}
public double getAnnualInterestRate(){
return annualInterestRate;
}
public void setId(int id){
this.id = id;
}
public void setBalance(double balance){
this.balance = balance;
}
public void setAnnualInterestRate(double annualInterestRate){
this. annualInterestRate = annualInterestRate;
}
public void withdraw(double amount){
if(amount > balance){
System.out.println("余额不足,取款失败");
return;
}
balance -= amount;
System.out.println("取款成功,取出:" + amount + "元");
}
public void deposits(double amount){
if(amount > 0){
balance += amount;
}
System.out.println("存款成功,存入:" + amount + "元");
}
}
class Customer{
private String firstName;
private String lastName;
private Account account;
public Customer(String firstName,String lastName){
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName(){
return firstName;
}
public String getLastName(){
return lastName;
}
public Account getAccount(){
return account;
}
public void setAccount(Account account){
this.account = account;
}
}
//> 按照如下图的UML类图,创建相应的类,提供必要的结构
//Account
//-balance:double
//+Account(init_balance):double
//+getBalance():double
//+deposit(amt:double)
//+withdraw(amt:double)
// > 在提款方法withdraw()中,需要判断用户余额是否能够满足提款数额的要求,如果不能,应该给出提示。deposit()方法表示存款
//> 按照如下图的UML类图,创建相应的类,提供必要的结构
//Customer
//-firstName:String
//-lastName:String
//-account:Account
//+Customer(f:String,l:String)
//+getFirstName():String
//+getLastName():String
//+getAccount():Account
//+setAccount(acct:Account)
//> 按照如下图的UML类图,创建相应的类,提供必要的结构
//Bank
//-customers:Customer[]
//-numberOfCustomer:int
//+Bank()
//+addCustomer(f:String,l:String)
//+getNumberOfCustomer();int
//+getCustomer(index:int):Customer
// > addCustomer方法必须依照参数(姓,名)构造一个新的Customer对象,然后把它放啊都customer数组中,还必须把numberOfCustomer属性值+1
// > getNumberOfCustomer方法返回numberOfCustomer属性值
// > getCustomer方法返回与给出的index参数相关的客户
//> 创建BankTest类进行测试
public class AccountCustomerBank{
public static void main(String[] args){
Bank bank = new Bank();
bank.addCustomer("Jane","Smith");
bank.getCustomer(0).setAccount(new Account(2000));
bank.getCustomer(0).getAccount().withdraw(500);
double balance = bank.getCustomer(0).getAccount().getBalance();
System.out.println("客户:" + bank.getCustomer(0).getFirstName() + " 的账户余额为:" + balance);
bank.addCustomer("三", "张");
System.out.println("银行客户的数量为:" + bank.getNumberOfCustomers());
}
}
class Account{
private double balance;
public Account(double init_balance){
this.balance = init_balance;
}
public double getBalance(){
return balance;
}
public double deposit(double amount){
if(amount > 0){
balance += amount;
}
System.out.println("存款成功,存入:" + amount + "元");
}
public void withdraw(double amount){
if(amount > balance){
System.out.println("余额不足,取款失败");
return;
}
balance -= amount;
System.out.println("取款成功,取出:" + amount + "元");
}
}
class Customer{
private String firstName;
private String lastName;
private Account account;
public Customer(String firstName,String lastName){
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName(){
return firstName;
}
public String getLastName(){
return lastName;
}
public Account getAccount(){
return account;
}
public void setAccount(Account account){
this.account = account;
}
}
class Bank{
private Customer[] customers;
private int numberOfCustomer;
public Bank(){
customers = new Customer[10];
}
public void addCustomer(String firstName,String lastName){
Customer c = new Customer(firstName,lastName);
customers[numberOfCustomer] = c;
numberOfCustomer++;
}
public int getNumberOfCustomer(){
return numberOfCustomer;
}
public Customer getCustomer(int index){
if(index >= 0 && index < numberOfCustomer){
return customers[index];
}
return null;
}
}