Java面向对象
编辑时间:2021/03/02
读完本节:大概花费40分钟,共4063词
1.方法参数的值传递机制(补充)
-
如下代码内存解析
public class ValueTransferTest(){ public static void main(String[] args){ String s1 = "hello"; ValueTransferTest test = new ValueTransferTest(); test.change(s1); System.out.println();//hello } public void change(String s){ s = "world"; } }
2.面向对象之封装与隐藏
-
程序设计的追求:高内聚、低耦合
高内聚:类的内部数据操作细节,类自己完成,不允许外部干预
低耦合:仅对外部暴露少量的方法用于使用
-
封装性的设计思想 :隐藏对象内部的复杂性,只对外部公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。
-
通过使用访问权限符来限定类内部数据xxx,通过setXxx()、getXxx()方法对数据进行操作,实现了对类的属性的封装。
package Encapsulation; public class AnimalTest { public static void main(String[] args){ Animal a = new Animal(); //由于权限修饰符,无法直接访问name属性 //a.name = "bobo"; //'name' has private access in 'Encapsulation.Animal' //通过set方法对对象的属性进行赋值,通过get方法获取对象的属性 a.setName("bobo"); System.out.println("名字是:" + a.getName()); /* 通过对属性的封装,可以实现对用户的输入进行合法性判定 在set方法内部可以设置对用户输入进行合法性判定*/ //动物的legs无法是负数 a.setLegs(-1); System.out.println(a.getLegs()); a.setLegs(2); System.out.println(a.getLegs()); } } class Animal{ private String name; private int age; private int legs; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getLegs(){ return legs; } public void setLegs(int legs){ if(legs >= 0 && legs % 2 == 0){ this.legs = legs; }else{ System.out.println("输入有误"); //在这里可以抛出异常 } } }
针对属性封装性的体现:
当我们创建了一个类的对象以后,我们可以通过“对象.属性”的方式,对对象的属性进行赋值。这里赋值操作要受到属性的数据和存储范围的制约。除此之外,没有其他的制约条件。但是在实际问题中,往往需要给属性赋值加入额外的限制条件。这个条件就不能在属性声明时体现,我们只能通过方法进行限制条件的添加(比如setXxx()方法)。同时,我们需要避免用户再继续使用“对象.属性”的方式对属性赋值,因此需要将属性设置为“private”。在对属性添加了权限修饰符之后,用户无法对该属性进行直接访问,这样造成了功能的缺失,因此需要一个getXxx()方法来获取对象的属性值。
-
封装性的体现一:
将类的属性xxx私有化(private),同时,提供公共的(public)方法来获取(getXxx())和设置(setXxx())。
封装性的体现二:
不对外暴露私有的方法,对于某些类比如说快排当中,排序时需要对数据进行交换,这个时候用户无需知晓交换的进行,只需要给出待排数据即可,因此可以对快排中的交换方法进行私有化,不需要对外暴露。
封装性的体现三:单例模式(将构造器私有化)
封装性的体现四:如果不希望在包外被调用则将类的权限设置为缺省的
封装性的体现还有很多…
3.权限修饰符
-
Java规定的四种权限(权限从小到大):private、缺省(default)、protected、public
用法:Java权限修饰符置于类的成员定义前,用来限定对象对该类成员的访问权限
权限修饰符修饰成员的可见性
修饰符 类内部 同一个包 不同包的子类 同一个工程 private true false false falses default ture true false false protected true true true false public true true true true 对于类的权限修饰符之可以使用public和缺省(default),因为如果使用其他的权限修饰符,导致这个类无法被其他的类访问。
public类可以在任意地方被访问;default类只能被同一个包的内部的类访问。
-
因为权限修饰符可以用于修饰类的成员,因此权限修饰符可以用来修饰类的内部结构:属性、方法、构造器、内部类
-
练习:
创建程序,在其中定义两个类:Person和PersonTest类。定义如下:用setAge()设置人的合法年龄,用getAge()返回人的年龄。在PersonTest类中实例化Person类的对象p,调用setAge()和getAge()方法。
package exer; public class Person { private int age; public void setAge(int age){ if(age > 0 && age < 130){ this.age = age; }else{ //不在范围内抛出异常 throw new RuntimeException("传入的数据非法"); } } public int getAge(){ return age; } }
package exer; public class PersonTest { public static void main(String[] args){ Person p = new Person(); p.setAge(21); System.out.println(p.getAge()); p.setAge(-1); } }
4.类的成员 - 构造器
-
构造器(constructor、构造方法)
构造器的作用:①创建对象(new + constructor);②初始化对象的属性
构造器的特征:①与类有相同的名称;②它声明返回值的类型(也不是声明为void);③不能被static、final、sychronized、abstract、native修饰,不能有return语句返回值。
-
1.如果没有显示定义类的构造器,JVM会提供一个默认的空参构造器;
2.定义构造器的格式
权限修饰符 类名(形参列表){}
3.一个类中定义的多个构造器,彼此之间构成重载;
4.一旦显示定义了类的构造器之后,JVM不再提供默认的空参构造器;
5.一个类中,一定存在至少一个构造器,要么是默认空参构造器,要么是用户定义的构造器哦~
-
练习1.在Person类中添加构造器,利用构造器设置所有人的age属性初始值都为18
2.在上述的类和构造器,增加name属性,使每次创建Person对象的同时初始化对象的age属性值和name属性值。
//1 package exer; public class Person { private int age; public Person(){ age = 18; } public void setAge(int age){ if(age > 0 && age < 130){ this.age = age; }else{ //不在范围内抛出异常 throw new RuntimeException("传入的数据非法"); } } public int getAge(){ return age; } } ****************************** package exer; public class PersonTest { public static void main(String[] args){ Person p = new Person(); System.out.println(p.getAge()); p.setAge(21); System.out.println(p.getAge()); // p.setAge(-1); } }
//2 package exer; public class Person { private int age; private String name; public Person(){ age = 18; } public Person(int age, String name){ setAge(age); setName(name); } public void setAge(int age){ if(age > 0 && age < 130){ this.age = age; }else{ //不在范围内抛出异常 throw new RuntimeException("传入的数据非法"); } } public int getAge(){ return age; } public void setName(String name){ this.name = name; } public String getName(){ return name; } } ************************* package exer; public class PersonTest { public static void main(String[] args){ Person p = new Person(); System.out.println(p.getAge()); p.setAge(21); System.out.println(p.getAge()); // p.setAge(-1); Person p1 = new Person(21, "bobo"); System.out.println("name:" + p1.getName() + ", age: " + p1.getAge()); } }
5.属性赋值过程
-
属性赋值的先后顺序
赋值的位置:①默认初始化;②显示初始化;③构造器中初始化;④通过“对象.属性”或“对象.方法”的方式赋值
赋值先后顺序:
① - ② - ③ - ④
其中④可以重复多次的为同一个属性进行赋值
6.JavaBean和POJO
-
JavaBean是一种Java语言写成的可重用组件,JavaBean的定义:
类是公共的;
有一个无参的构造器
有属性,且有对应的get、set方法
-
用户可以使用JavaBean将功能、处理、值、数据库访问和其他任何可以用Java代码创造的对象进行打包,并且其他的开发者可以通过内部的JSP页面、Servlet、其他JavaBean、applet程序或者应用来使用这些对象,用户可以认为JavaBean提供了一种随时随地的复制和粘贴的内容,而不用关心任何改变
-
POJO(Plain Ordinary Java Object)是指普通Java类,具有一部分的get、set方法。
-
JavaBean与POJO的关系:
当一个Pojo可序列化,有一个无参的构造函数,使用getter和setter方法来访问属性时,他就是一个JavaBean。
7.UML类图
8.this关键字
-
this关键字的使用:this关键字可以用来修饰属性、方法、构造器
-
this修饰属性和方法:此时this理解为”当前对象“或”正在创建的对象“
- 在类的方法中,可以通过使用”this.属性“或”this.方法“的方式,调用当前对象属性或方法。但是通常情况下都可以省略”this.“。特殊情况下,如果方法的形参和类的属性同名时,就必须显示的使用”this.变量“的方式,表明此变量是属性,而非形参。
- 在类的构造器中,可以通过使用”this.属性“或”this.方法“的方式,调用当前对象属性或方法。但是通常情况下都可以省略”this.“。特殊情况下,如果构造器的形参和类的属性同名时,就必须显示的使用”this.变量“的方式,表明此变量是属性,而非形参。
-
this调用构造器
- 在类的构造器中,可以显示的使用”this(形参列表)“方式,调用指定的类的构造器
- 构造器中不能通过”this(形参列表)“方式调用自己
- 如果一个类中有n个构造器,则最多有n - 1个构造器可以使用”this(形参列表)“
- this(形参列表)必须声明在当前构造器的首行。
- 一个构造器中最多只能声明一个”this(形参列表)“来调用其他的构造器
package exer; public class Person { private int age; private String name; public Person(){ //在其他的构造器中使用this(形参列表)就不用重复声明,造成冗余 System.out.println("假设这里有很多行的初始化语句"); age = 18; } public Person(String name){ //直接复用Person(){}构造器 this(); setName(name); } public Person(int age){ //直接复用Person(){}构造器 this(); setAge(age); } public Person(int age, String name){ //有了this(age)相当于直接复用了Person(age){}构造器 this(age); // setAge(age); setName(name); } public void setAge(int age){ if(age > 0 && age < 130){ this.age = age; }else{ //不在范围内抛出异常 throw new RuntimeException("传入的数据非法"); } } public int getAge(){ return age; } public void setName(String name){ this.name = name; } public String getName(){ return name; } } //******************************另一文件*************************** package exer; public class PersonTest { public static void main(String[] args){ Person p = new Person(); System.out.println(p.getAge()); p.setAge(21); System.out.println(p.getAge()); // p.setAge(-1); Person p1 = new Person(21, "bobo"); System.out.println("name:" + p1.getName() + ", age: " + p1.getAge()); //************************ System.out.println("**************************"); Person p2 = new Person("biggy"); System.out.println("name:" + p2.getName()); } }
-
练习
- addCustomer方法必须依照参数(名、姓)构造一个新的Customer对象,然后把它放到customer数组中。还必须把numberOfCustomer属性的值加1
- getNumberOfCustomer方法返回numberOfCustomers属性值
- getCustomer方法返回给出index参数的相关的客户
- 创建BankTest类,进行测试
package bank; public class Account { private double balance; public Account(double init_balance){ balance = init_balance; } public double getBalance() { return balance; } public void deposit(double amt){ balance += amt; } public void withdraw(double amt){ if(amt > balance){ System.out.println("余额不足"); }else{ balance -= amt; } } }
package bank; public class Customer { private String firstName; private String lastName; private Account account; public Customer(String f, String l){ this.firstName = f; this.lastName = l; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public Account getAccount() { return account; } public void setAccount(Account acct) { this.account = acct; } }
package bank; public class Bank { private Customer[] customers; private int numberOfCustomer; public Bank(){ //初始化用户数组 customers = new Customer[10]; } //lastName姓、firstName名,银行添加用户 public void addCustomer(String f, String l){ Customer cust = new Customer(f, l); customers[numberOfCustomer] = cust; numberOfCustomer++; //或 //customers[numberOfCustomer++] = cust; } //获取客户的个数 public int getNumberOfCustomer(){ return numberOfCustomer; } //获取指定客户的信息 public Customer getCustomer(int index){ if(index < numberOfCustomer && index >= 0){ //当角标满足在用户数组的范围内则返回指定用户 return customers[index]; }else{ //否则返回空 return null; } } }
package bank; public class BankTest { public static void main(String[] args){ double balance; //实例化Bank Bank bank = new Bank(); //为bank添加新用户 bank.addCustomer("bob","ma"); /*通过bank对象下的获取指定用户的信息, * 为这个用户添加新账户, * 并且这个新账户的初始余额是1000 * */ //通过匿名对象创建新账户 bank.getCustomer(0).setAccount(new Account(1000)); //获取0号用户的信息后,存入500 bank.getCustomer(0).getAccount().deposit(500); //获取用户当前余额 balance = bank.getCustomer(0).getAccount().getBalance(); //输出目前账户信息 System.out.println("客户 " + bank.getCustomer(0).getFirstName() + " 的账户余额为:" + balance); //获取0号用户的信息后,取入1200 bank.getCustomer(0).getAccount().withdraw(1200); //获取用户当前余额 balance = bank.getCustomer(0).getAccount().getBalance(); //输出目前账户信息 System.out.println("客户 " + bank.getCustomer(0).getFirstName() + " 的账户余额为:" + balance); //输出目前银行的客户个数 System.out.println("银行的客户个数为:" + bank.getNumberOfCustomer()); } }
9.package关键字
-
为了更好的实现项目中类的管理,java提供包的概念;
-
使用package声明类或接口所属的包,声明在该文件的首行;
-
包——标识符,应当遵循标识符的命名规则(详情见文章20210103Java基础文末),做到见名知义。包命名的规范(xxx.yyy.zzz小写)
-
每”.“一次代表一层文件目录
-
同一个包下不能命名同名的接口、类;不同的包下可以命名同名的接口、类,但在同一个文件中引用不同包下的同名接口、类需要至少一个要以全类名的方式显示
-
JDK中主要的包
java.lang——包含一些Java语言的核心类,如String、Math、Integer、System和Thread,提供常用功能
java.net——包含执行与网络相关炒作的类和接口
java.io——包含能够提供多种输入、输出功能的类
java.util——包含一些实用工具类,如定义系统特性、接口的集合框架类、使用与日期日历相关的函数
java.text——包含一些java格式化相关的类
java.sql——包含了java进行JDBC数据库编程的相关类、接口
10.import关键字
-
import的作用,在一个java文件中导入其他已有的包
-
使用注意事项:
-
在源文件中显示使用的import结构导入指定包下的类、接口;
-
import声明在包的声明和类的声明之间;
-
如果需要导入多个结构,并列写出即可;
-
可以使用”xxx.*“的方式标识导入xxx包下的所有结构;
-
如果使用的类或接口时java.lang包下定义的,则可以省略import结构;
-
如果使用的类或接口时本包下定义的,则可以省略import结构;
-
如果在源文件中,使用了不同包下的同名类,则必须至少有一个类需要以全类名的方式显示;
-
使用”xxx.*“方式表明可以调用xxx包下的所有结构,但是如果使用的是xxx子包下的结构,则仍需要显示import;
-
import static:导入指定的类或接口中的静态结构
import static java.lang.System.out; public class importStaticTest { public static void main(String[] args){ out.println("STAY ANGER!!!"); } }
-
11.MVC设计模式
-
MVC是常用的设计模式之一,将整个程序分为三个层次:视图模型层、控制器层与数据模型层。这种将程序输入输出、数据处理,以及数据展分离开来的设计模式使程序结构变得灵活而且清晰,同时也描述了程序各个对象之间的通信方式,降低了程序的耦合性。
-
模型层(model):主要处理数据
数据对象封装 model.bean/domain
数据库操作类 model.dao
数据库 model.db
视图层(view):显示数据
相关工具类 view.util
自定义view view.ui
控制层(controller):业务处理层
应用界面相关 controller.activity
存放fragment controller.fraagment
显示列表的适配器 controller.adapter
服务相关 controller.service
抽取的基类 controller.base