JAVA面向对象编程(上)
JAVA面向对象编程(上)(1)
---POP与OOP的差别、OOP三大特征、OOP两个要素、类和对象的使用、对象的内存解析、类中属性的使用、类中方法的声明和使用
- 面向过程(POP)与面向对象(OOP)的差别
二者都是一种思想,面向对象是相对于面向过程而言的。面向过程,强调的是功能行为,以函数为最小单位,考虑“怎么做?”。面向对象将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑“谁来做?”。
- 面向对象的三大特征:
封装(Encapsulation)
继承(Inheritance)
多态(Polymorphism)
- 面向对象的两个要素:
(Class)和对象(Object)是面向对象的核心概念。
类是对一类事物的描述,是抽象、概念上的定义
对象是实际存在的该类事物的每个个体,因而也称为实例(instance)。”万事万物皆对象“
面向对象的重点就是类的设计,其实也就是类的成员的设计。
类的成员:
*属性=成员变量(field)
*方法=成员方法(method)
构造器
代码块
内部类
一、类和对象的使用
1.使用方法:
- 创建类、设计类的成员。
- 创建类的对象。
- 通过“对象.属性名“和”对象.方法名()“调用对象的结构。
代码实例:
public class OOPTest_Person {
public static void main(String[] args) {
//2.创建Person类的对象
Person pers = new Person();
//调用对象的结构:属性、方法
//调用属性:对象.属性名
pers.name = "马云";
pers.age = 30;
pers.isMale = true;
//调动方法:对象.方法名()
pers.eat();
pers.sleep();
pers.talk("中文");
System.out.println("他的名字是" + pers.name
+ "\t他的年龄是" + pers.age + "岁\t是否为男性:" + pers.isMale);
}
}
class Person{
//属性
String name;
int age;
boolean isMale;
//方法
public void eat(){
System.out.println("他可以吃饭");
}
public void sleep(){
System.out.println("他可以睡觉");
}
public void talk(String language){
System.out.println("他可以使用" + language + "交流");
}
}
运行结果:

-
如果创建了一个类的多个对象,则每个对象都独立的拥有一套类的属性。(非static)
如果我们修改一个对象的属性a,则不影响另外一个对象属性a的值。
-
特别的,如果我们创建一个新的对象pers_t,并将pers的地址赋给pers_t,那么它们就可以共享同一个属性值。
Person pers_t = new Person();
pers_t = pers;//将pers变量保存的对象地址值赋给pers_t,导致它们两个指向了堆空间中的同一个对象实体
pers_t.age = 10;
System.out.println(pers.age);
运行结果:

2.对象的内存解析


3.类中属性的使用
- 属性(也叫做成员变量)与局部变量的异同点:
-
相同点:
①声明和赋值的格式相同:数据类型 变量名 = 变量值。
②先声明,后使用。
③都有其对应的作用域。
-
不同点:
①声明的位置不同
属性:直接声明在类{}中。
局部变量:声明在方法中、方法形参、代码块内、构造器形参、构造器内部的变量。
②权限修饰符不同
属性:可以在声明时,指明其权限,使用权限修饰符。
---常用的权限修饰符:private、public、缺省、protected。
局部变量:不可以使用权限修饰符。
③默认初始化值不同
属性:类的属性,根据其类型,都有默认初始化值。
---整型0、浮点型0.0、字符型0、布尔型false、引用数据类型null
局部变量:没有默认初始化值
在调用局部变量之前,一定要显式赋值。
特别的,形参在调用时赋值,如上方Person类中的language。
④在内存中加载的位置不同
属性:加载到堆空间中(非static)。
局部变量:加载到栈空间中。
4.类中方法的声明和使用
- 方法:描述类应该具有的功能。
- 方法的声明:权限修饰符 返回值类型 方法名(形参列表){方法体}
-
权限修饰符:private、public、缺省、protected。
-
返回值类型:
①有返回值
如果方法有返回值,则必须在声明时,指定返回值类型。同时,方法中,需要使用return来返灰指定类型的数据,即“return 数据;”。
②没有返回值
如果方法没有返回值,啧在方法声明时,使用void来表示。通常没有返回值的方法中,不使用return,如果使用,仅表示结束此方法,且后面不加数据,即“return;”即可。
3.形参列表:
方法可以声明0个或多个形参
格式:(数据类型1 形参1,数据类型2 形参2,.....,数据类型n 形参n)形参可以为数组。
-
return关键字的使用
1.使用范围:方法体中
2.作用:
①结束方法。
②针对有返回值类型的方法,返回所要类型的数据。
-
方法的使用
可以调用当前类的属性和方法。
---特别的,方法A中又调用了方法A,我们称之为递归方法。
-
例题1:
定义类Student,包含三个属性:学号int number,年级int state,成绩int score。
创建20个学生对象,学号为120,年级为(16)和成绩(0~100)都由随机数确定。
问题一:打印出指定年级学生的信息。
问题二:使用冒牌排序按学生成绩排序,并遍历所有学生信息。
代码如下:
import java.util.Scanner; public class StudentTest { public static void main(String[] args) { //创建对象 Student[] stud = new Student[20]; for(int i = 0;i < stud.length;i++){ //给数组元素赋值 stud[i] = new Student(); stud[i].number = i+1; //年级[1-6] stud[i].state = (int)(Math.random() * (6 - 1 + 1) + 1); //成绩[0-100] stud[i].score = (int)(Math.random() * (100 - 0 + 1) + 0); } //创建Student对象 Student stude = new Student(); //因为print()不是static静态方法,所以需要创建一个Studet类对象来调用 stude.print(stud); //调用printState()方法输出指定年级学生的信息 Student.printState(stud); //调用sort()方法对学生成绩进行排序 Student.sort(stud); } } class Student{ int number;//学号 int state;//年级 int score;//成绩 // 遍历输出 public void print(Student[] stud){ System.out.println("学号\t年级\t成绩"); for(int i = 0;i < stud.length;i++){ System.out.println(stud[i].number + "\t" + stud[i].state + "\t" + stud[i].score); } System.out.println(); } //输出指定年级学生的成绩 public static void printState(Student[] stud){ System.out.println("请输入想输出的年级"); Scanner scan = new Scanner(System.in); int state = scan.nextInt(); System.out.println(state + "年级学生的成绩如下"); System.out.println("学号\t年级\t成绩"); for(int i = 0;i < stud.length;i++){ if(stud[i].state == state) { System.out.println(stud[i].number + "\t" + stud[i].state + "\t" + stud[i].score); } } } //按成绩由低到高使用冒泡排序 public static void sort(Student[] stud) { Student temp = new Student(); for (int i = 0; i < stud.length - 1; i++) { for (int j = 0; j < stud.length - 1 - i; j++) { if (stud[j].score > stud[j + 1].score) { //直接交换对象 temp = stud[j]; stud[j] = stud[j + 1]; stud[j + 1] = temp; } } } System.out.println(); System.out.println("按成绩从小到大排序:"); System.out.println("学号\t年级\t成绩"); for (int i = 0; i < stud.length; i++) { System.out.println(stud[i].number + "\t" + stud[i].state + "\t" + stud[i].score); } } }
5、匿名对象的使用
①理解:我们创建的对象,没有显式的赋给一个变量名,即为匿名对象
②特征:匿名对象只能调用一次
③使用:代码举例
public class InstanceTest {
public static void main(String[] args) {
Phone p = new Phone();
System.out.println(p);
p.sendEmail();
p.playGame();
//匿名对象
new Phone().sendEmail();
new Phone().playGame();
//两个匿名对象不是同一个对象
//*********匿名对象的使用************
PhoneMall mall = new PhoneMall();
mall.show(new Phone());
}
}
class PhoneMall{
public void show(Phone phone){
phone.sendEmail();
phone.playGame();
}
}
class Phone{
double price;//价格
public void sendEmail(){
System.out.println("发送邮件");
}
public void playGame(){
System.out.println("玩游戏");
}
}
6、方法的重载(overload)
概念:在同一个类中,允许存在一个以上的同名方法,只要他们的参数个数或者类型不同即可。“两同一不同”:同一个类,同一个方法名,不同参数列表
特点:与返回值类型无关,只看参数列表,且参数列表(参数个数、类型)必须不同。调用时,根据方法参数列表的不同来区别。
7、可变个数的形参
JavaSE5.0中提供了Varargs(variable number of arguments)机制,允许直接定义能和多个实参相匹配的形参。从而,可以用一种更简单的方式,来传递个数可变的实参。
①可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载
②可变个数形参的方法与本类中方法名相同,形参类型也相同的数组之间不构成重载,不能共存。例如:public void show(String...strs){}与public void show(String[] strs){}不构成重载。
③可变个数形参在方法的形参中,最多只能声明一个可变个数形参。
④可变个数形参在方法的形参中,必须声明在末尾,例如public void show(String ...strs,int i){}是不符合要求的。
代码举例:
public class MethodArgsTest {
public static void main(String[] args) {
MethodArgsTest test = new MethodArgsTest();
test.show();
test.show("hallow");
test.show("world","fun","happy");
}
public void show(String s){
System.out.println("普通形参");
}
public void show(String...strs){
System.out.println("可变个数形参");
}
//public void show(string[] strs){}//与可变个数形参不可共存
}
运行结果:

8、方法参数的值传递机制
形参:方法声明时小括号内的参数
实参:方法调用时实际传给形参的参数值
Java的实参值如何传入方法呢?
Java里方法的参数传递方式只有一种:值传递。即将实际参数值的副本(复制品)传入方法内,儿参数本身不受影响。
-
值传递机制:
①形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参。
②形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参。
9、递归方法
①递归方法:一个方法体内调用它自身
②方法递归包含一种隐式的循环,他会重复执行某段代码,但这种重复无需循环控制。递归一定要向已知方法递归,否则就变成了无穷递归,类似于死循环。
Java面向对象编程(上)(2)
OOP特征之一:封装与隐藏、权限修饰符、构造器、关键字this/package/import
二、面向对象特征之一:封装与隐藏
我们程序设计要求:“高内聚、低耦合”
高内聚:类的内部数据操作细节自己完成,不允许外部干涉;
低耦合:仅对外暴露少量的方法用于使用。
隐藏对象内部的复杂性,值对外公开简单的接口。便于外界调用,从而提高系统的可拓展性、可维护性。通俗地说,把该隐藏的隐藏起来,该暴露的暴露出来,这就是封装性的设计思想。
封装性的体现之一:
①我们将类的属性(xxx)私有化(private),同时提供公共的方法来获取(getXxx)和设置(setXxx)属性的值。
拓展:封装性的其他体现:②不对外暴露的私有方法,③单例模式(将构造器私有化)等等
权限修饰符
①Java4种权限修饰符(从小到大排列):私有(private)、缺省(default)、保护(protected)、公有(public)
②4种权限修饰符可以用来修饰类及类的内部结构:属性、方法、构造器、内部类。
③具体的,4种权限都可以用来修饰类的内部结构,修饰类的话,只能使用缺省和public。

总结封装性
Java提供了4种权限修饰符来修饰类及类的内部结构,体现类及类的内部结构在被调用时的可见性的大小
1.构造器(或构造方法)constructor
①构造器的作用:
- 创建对象,例:Person p = new Person(); 类名 对象名 = new 此类的构造器;
- 初始化对象的信息
②定义构造器的格式:权限修饰符 类名(形参列表){}
③一个类中定义的多个构造器,彼此构成重载
说明:如果没有显式的定义类的构造器,系统将默认提供一个空参的构造器,此构造器的权限同类的权限;一旦我们显式的定义了类的构造器之后,系统就不再提供默认的空参构造器;也说明,一个类中至少会有一个构造器。
构造器与普通方法的差别:没有返回值类型(void、int等)
总结:属性赋值的先后顺序
①默认初始化(int age;)
②显式初始化(int age = 1;)
③构造器中初始化(Person per = new Person(2);)
④“对象.方法”和"对象.属性"赋值(per.setAge(3)😉
以上操作赋值的先后顺序:①②③④
拓展知识:JavaBean
-
JavaBean是一种Java语言写成的可重用组件。
-
所谓JavaBean,是指符合如下标准的Java类:
①类是公共的
②有一个无参的公共的构造器
③有属性,且有对应的get、set方法
-
用户可以使用JavaBean将功能、处理、值、数据库访问和其他任何可以用Java代码创造的对象进行打包,并且其他的开发者可以通过内部的JSP页面、Servlet、其他JavaBean、applet程序或者应用来使用这些对象。用户可以认为JavaBean提供了一种可以随时随地的复制和粘贴的功能,而不用关心任何变化。
拓展知识:UML类图

例题:
//编写两个类,其中TriAngle类中声明私有的底边长base和高Height,同时声明公共方法访问私有
// 变量此外,提供类必要的构造器。另外一个类中使用这些公共方法,计算三角形的面积。
public class TriAngle {
private double base;
private double height;
public TriAngle(){//空参构造器
}
public TriAngle(double b,double h){
base = b;
height = h;
}
public void setBase(double b){
base = b;
}
public double getBase(){
return base;
}
public void setHeight(double h){
height = h;
}
public double getHeight(){
return height;
}
public double getArea(){
return (base*height)/2.0;
}
}
public class TriAngleTest {
public static void main(String[] args) {
TriAngle t1 = new TriAngle();//空参构造器
t1.setBase(3.0);
t1.setHeight(5.0);
System.out.println("base = " + t1.getBase() + ",height = "
+ t1.getHeight() + ",Area = " + t1.getArea());
TriAngle t2 = new TriAngle(3.0,5.0);
System.out.println("base = " + t2.getBase() + ",height = "
+ t2.getHeight() + ",Area = " + t2.getArea());
}
}
2.关键字:this的使用
它在方法内部使用,即这个方法所属对象的引用;
它在构造器内部使用,表示该构造器正在初始化的对象。
具体的:我们可以用this来区分局部变量和属性
比如:this.name(属性) = name(形参);
this可以用来修饰、;属性、方法、构造器;
在类的方法中,我们可以使用“this.属性”或“this.方法”的方法,调用当前对象属性或方法;通常情况下,我们都选择省略“this.”,特殊的,如果方法的形参和类的属性同名时,我们必须显式的使用“this.变量”的方式,表明此变量是属性而非形参。
在类的构造器中,我们可以使用“this.属性”或“this.方法”的方法,调用当前正在创建的属性或方法;通常情况下,我们都选择省略“this.”,特殊的,如果方法的形参和类的属性同名时,我们必须显式的使用“this.变量”的方式,表明此变量是属性而非形参。
this调用构造器
①我们在类的构造器中,可以显式的使用“this(形参列表)”方式,调用本类中指定的其他构造器,不能调用自己。
例:
public Person(){
//this();错误,不能自己调用自己
}
public Person(String name){
this();//调用无参构造方法
}
②如果一个类中又n个构造器,则最多有n-1构造器中使用了“this(形参列表)”,避免陷入“你调用我,我调用你”的死循环中。
③规定:“this(形参列表)”必须声明在当前构造器的首行,也说明在构造器内部,最多只能声明一个“this(形参列表)",用来调用其他的构造器。
例题Bank:
public class Account {//账户
private double balance;
public Account(double balance){
this.balance = balance;
}
public double getBalance() {
return balance;
}
public void deposit(double amt){//存钱
if(amt > 0) {
this.balance += amt;
System.out.println("存款成功!存入" + amt + "元。");
}
else{
System.out.println("输入有误!");
}
}
public void withdraw(double amt){//取钱
if(amt <= balance){
this.balance -= amt;
System.out.println("取款成功!取出" + amt + "元。");
}
else{
System.out.println("取款失败!余额不足!");
}
}
}
public 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;
}
}
public class Bank {//银行
private Customer[] customers;//存放多个客户
private int numberOfCustomers;//记录客户的个数,int型默认0
public Bank(){
customers = new Customer[50];
}
public void addCustomer(String firstName,String lastName){//添加用户
Customer customer = new Customer(firstName,lastName);
customers[numberOfCustomers++] = customer;//调一次,numberOfCustomer自加1
}
public Customer getCustomer(int index) {//获取index位置客户
if(index >= 0 && index < numberOfCustomers){
return customers[index];
}
return null;
}
public int getNumberOfCustomer() {//获取客户的个数
return numberOfCustomers;
}
}
public class BankTest {//测试类
public static void main(String[] args) {
Bank bank = new Bank();
bank.addCustomer("Jane","Smith");//添加用户Jane·Smith
bank.getCustomer(0).setAccount(new Account(10000));//创建Jane的银行账户,初始存款10000
bank.getCustomer(0).getAccount().withdraw(2000);//取钱2000
bank.getCustomer(0).getAccount().deposit(5000);//存5000
//打印余额
System.out.println("客户:" +bank.getCustomer(0).getFirstName() + "账户余额为"
+ bank.getCustomer(0).getAccount().getBalance() + "元。");
bank.addCustomer("nima","wang");//添加用户wangnima
System.out.println("银行客户数为:" + bank.getNumberOfCustomer());
}
}
3.关键字:package、import的使用
package:为了更好的实现项目中类的管理,提供包(package)的概念
①使用package声明类或接口所属的包,声明在源文件的首行
②package属于标识符,遵循标识符的命名规则、规范(xxxyyyzzz)、”见名知意“
③package名称每出现一个“.”,表明一层文件目录,例:package com.atguigu.javaProject01,表明com目录中atguigu目录下的javaProject01,同一个包下,不能命名同名的接口、类。

import:在源文件中显式的使用import结构导入指定包下的类、接口
①声明在包的声明和类的声明之间。
②可以使用“xxx.*"的方式,导入xxx包下的所有结构,但是如果使用的是“xxx”子包下的结构,任然需要显式导入。
③java.lang包是java核心包,程序会自动导入,调用此包的结构(如String/system等)可以省略import。
④调用本包下定义的类或接口,可以省略import。
⑤如果在源文件中,使用了不同的包下同名的类,则必须至少有一个类需要以全类名的方式显示。
例:
⑥import static:导入指定类或接口中的静态结构(属性、方法)。

浙公网安备 33010602011771号