面向对象(2)
1 成员方法
-
方法是类或对象行为特征的抽象,用来完成某个功能操作。在某些语言中也称为函数或过程。
-
将功能封装为方法的目的是,可以实现代码重用,简化代码
-
Java里的方法不能独立存在,所有的方法必须定义在类里。
1.1 方法的声明格式
修饰符 返回值类型 方法名(参数类型 形参1, 参数类型 形参2, ….){
方法体程序代码
return 返回值;
}
修饰符:public,缺省,private, protected等
返回值类型:
-
没有返回值:void。
-
有返回值,声明出返回值的类型。与方法体中“return返回值”搭配使用
方法名:属于标识符,命名时遵循标识符命名规则和规范,“见名知意” ,最好以动词开头
形参列表:可以包含零个,一个或多个参数。多个参数时,中间用“,”隔开
返回值:方法在执行完毕后返还给调用它的程序的数据。
1.2 方法的调用
方法通过方法名被调用,且只有被调用才会执行。
对象.方法名()
Car c=new Car();
c.getname();
new Car().getname();
-
方法被调用一次,就会执行一次
-
没有具体返回值的情况,返回值类型用关键字void表示,那么方法体中可以不必使用return语句。如果使用,仅用来结束方法。
-
定义方法时,方法的结果应该返回给调用者,交由调用者处理。
-
方法中只能调用方法或属性,不可以在方法内部定义方法。
课堂练习:
利用面向对象的编程方法,设计类Circle计算圆的面积。
Math.PI * r * r
package Chapter02.day03.day04;
import java.util.Scanner;
public class Circle {
public double area(double r){
return Math.PI*r*r;
}
public static void main(String[] args) {
Circle c=new Circle();
System.out.println("输入半径:");
Scanner input=new Scanner(System.in);
double r=input.nextDouble();
System.out.println("面积为:"+c.area(1));
}
}
课堂练习:
定义类Student,包含三个属性:学号number(int),年级state(int),成绩score(int)。 创建20个学生对象,学号为1到20,年级[1,6]和成绩[0,100]都由随机数确定。
问题一:打印出3年级(state值为3)的学生信息。
问题二:使用冒泡排序按学生成绩排序,并遍历所有学生信息
1.3 方法重载
方法重载(overload):在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。
重载的特点:
- 与返回值类型无关,只看参数列表,且参数列表必须不同。(参数个数或参数类型)。
- 调用时,根据方法参数列表的不同来区别。
实例代码
int[] arr1 = new int[]{1,2,3,4};
char[] arr2 = new char[]{'a','b','c','d'};
System.out.println(arr1);
System.out.println(arr2);
1.4 可变长度的形参列表
JavaSE 5.0 中提供了**Varargs(variable number of arguments)**机制,允许直接定义能和多个实参相匹配的形参。从而,可以用一种更简单的方式,来传递个数可变的实参。
-
JDK 5.0以前:采用数组形参来定义方法,传入多个同一类型变量
public static void test(int a ,String[] books);
-
JDK5.0:采用可变个数形参来定义方法,传入多个同一类型变量
public static void test(int a ,String…books);
特点
- 声明格式:方法名(参数的类型名 …参数名) 可以当作数组使用;(掌握)
- 可变参数:方法参数部分指定类型的参数个数是可变多个:0个,1个或多个
- 可变个数形参的方法与同名的方法之间,彼此构成重载
- 可变参数方法的使用与方法参数部分使用数组是一致的
- 方法的参数部分有可变形参,需要放在形参声明的最后
- 在一个方法的形参位置,最多只能声明一个可变个数形参
1.5 方法参数的传递机制
形参:方法声明时的参数
**实参:**方法调用时实际传给形参的参数值
Java里方法的参数传递方式只有一种:值传递。 即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响。
-
形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参
-
形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参
思考题
定义一个int型的数组:int[] arr = new int[]{12,3,3,34,56,77,432};让数组的每个位置上的值去除以首位置的元素,得到的结果,作为该位置上的新值。遍历新的数组。
//错误写法
for(int i= 0;i < arr.length;i++){
arr[i] = arr[i] / arr[0];
}
//正确写法1
for(int i = arr.length – 1;i >= 0;i--){
arr[i] = arr[i] / arr[0];
}
//正确写法2
int temp = arr[0];
for(int i= 0;i < arr.length;i++){
arr[i] = arr[i] / temp;
}
2 static关键字
方法区又分为:静态区(常量池)和字节码
静态区:其中static关键字修饰的变量方法存放在其中。static关键字修饰的变量方法不属于任何类。
字节码:.class文件
static修饰的变量和方法:可直接类名.方法名 调用
在Java语言中,static表示“静态”的意思,使用场景可以用来修饰成员变量和成员方法,当然也可以是静态代码块。static的主要作用在于创建独立于具体对象的域变量或者方法。在Java类中,可用static修饰属性、方法、代码块、内部类
被修饰后的成员具备以下特点
-
随着类的加载而加载
-
优先于对象存在
-
修饰的成员,被所有对象所共享
-
访问权限允许时,可不创建对象,直接被类调用
2.1 修饰变量
当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份。如果想让一个类的所有实例共享数据,就用类属性!
语法格式
static int count = 0;
特点
- 类属性作为该类各个对象之间共享的变量。在设计类时,分析哪些属性不因对象的不同而改变,将这些属性设置为类属性。相应的方法设置为类方法。
2.2 修饰方法
如果方法与调用者无关,则这样的方法通常被声明为类方法,比如说工具方法由于不需要创建对象就可以调用类方法,从而简化了方法的调用。
作用:一般用于封装不需要创建对象就能使用的功能,比如说工具类。
语法格式
static int showCount() {
return count;
};
特点
- 没有对象的实例时,可以用类名.方法名()的形式访问由static修饰的类方法。
- 在static方法内部只能访问类的static修饰的属性或方法,不能访问类的非static的结构。
static:对象共有的
其他:对象特有
课堂练习:
编写一个类实现银行账户的概念,包含的属性有“帐号”、“密码”、“存款余额”、“利率”、“最小余额”,定义封装这些属性的方法。
编写测试类,使用银行账户类,输入、输出3个储户的上述信息。
考虑:哪些属性可以设计成static属性。
class Account {
public String number;
public String password;
public double balance;
public static double raise;
public static double minBalance;
}
//***********************//
public class Account {
private int account_number;//账号
private int password;//密码
private double deposit_balance;//存款余额
private double interest_rate;//利率
private double min_balance;//最小余额
static int final_number=2019001;
{
account_number = final_number;
System.out.printf("账号为:%d\n", account_number);
final_number++;
}
public int getAccount_number() {
return account_number;
}
public void setAccount_number(int account_number) {
this.account_number = account_number;
}
public int getPassword() {
return password;
}
public void setPassword(int password) {
this.password = password;
}
public double getDeposit_balance() {
return deposit_balance;
}
public void setDeposit_balance(double deposit_balance) {
this.deposit_balance = deposit_balance;
}
public double getInterest_rate() {
return interest_rate;
}
public void setInterest_rate(double interest_rate) {
this.interest_rate = interest_rate;
}
public double getMin_balance() {
return min_balance;
}
public void setMin_balance(double min_balance) {
this.min_balance = min_balance;
}
@Override
public String toString() {
return "Account [账号" + account_number + ", 密码" + password + ", 存款余额"
+ deposit_balance + ", 利率" + interest_rate + ", 最小余额" + min_balance + "]";
}
}
import java.util.Scanner;
public class MainClass {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner reader = new Scanner(System.in);
Account user1 = new Account();
Account user2 = new Account();
Account user3 = new Account();
System.out.println("设置密码:");
user1.setPassword(reader.nextInt());
user2.setPassword(reader.nextInt());
user3.setPassword(reader.nextInt());
System.out.println("输入存款:");
user1.setDeposit_balance(reader.nextDouble());
user2.setDeposit_balance(reader.nextDouble());
user3.setDeposit_balance(reader.nextDouble());
System.out.println("输入利率:");
user1.setInterest_rate(reader.nextDouble());
user2.setInterest_rate(reader.nextDouble());
user3.setInterest_rate(reader.nextDouble());
System.out.println("输入最小余额:");
user1.setMin_balance(reader.nextDouble());
user2.setMin_balance(reader.nextDouble());
user3.setMin_balance(reader.nextDouble());
System.out.println(user1.toString()+'\n'+user2.toString()+'\n'+user3.toString());
}
}
2.3 静态代码块
在开发中,有时需要某些代码在程序启动的时候就执行,这时候就需要静态代码块。比如一个项目启动需要加载的很多配置文件等资源,我们就可以都放入静态代码块中。
语法格式
static {}
特点
- 静态代码块在类被加载的时候就运行了,而且只运行一次
- 静态代码块不能存在任何方法体中
- 静态代码块不能访问成员变量
非静态代码块
1.能够访问到那些结构?静态与非静态都能够被访问到
2.作用:初始化成员变量
3.代码块的执行时机。代码块的代码不是通过。方式执行,而是jvm在创建对象的时候执行(创建一次,执行一次。它的生命周期和对象绑定一起的)。
静态代码块
1.能够访问到那些结构?静态结构
2.代码块的执行时机。代码块中的代码不是通过。方式执行,而是jvm在加载类的时候自动执行
3.初始化类变量,加载外部资源
3 封装
3.1 封装概述
面向对象编程语言是对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界无法直接操作和修改。封装可以被认为是一个保护屏障,防止该类的代码和数据被其他类随意访问。要访问该类的数据,必须通过指定的方式。适当的封装可以让代码更容易理解与维护,也加强了代码的安全性。
实现原则
- 将成员隐藏起来,若需要访问某个成员,提供公共方法对其访问。
3.2 封装步骤
-
使用
private
关键字来修饰成员变量。 -
对需要访问的成员变量,提供对应的一对
getXxx
方法 、setXxx
方法。
特点
-
隐藏一个类中不需要对外提供的实现细节;
-
使用者只能通过事先定制好的方法来访问数据,可以方便地加入控制逻辑,限制对属性的不合理操作;
-
便于修改,增强代码的可维护性;
private关键字
-
private是一个权限修饰符,代表最小权限。
-
可以修饰成员变量和成员方法。
-
被private修饰后的成员变量和成员方法,只在本类中才能访问。
示例代码
public class Student {
private String name;
private int age;
public void setName(String n) {
name = n;
}
public String getName() {
return name;
}
public void setAge(int a) {
age = a;
}
public int getAge() {
return age;
}
}
四种访问权限修饰符
Java权限修饰符public、protected、(缺省)、private置于类的成员定义前,用来限定对象对该类成员的访问权限。
修饰符 | 类内部 | 同一个包 | 不同包的派生类 | 同一工程 |
---|---|---|---|---|
private | yes | |||
缺省 | yes | yes | ||
protected | yes | yes | yes | |
public | yes | yes | yes | yes |
对于class的权限修饰只可以用public和default(缺省)。
-
public类可以在任意地方被访问。
-
default类只可以被同一个包内部的类访问。
3.3 this关键字-重点
我们发现setXxx
方法中的形参名字并不符合见名知意的规定,那么如果修改与成员变量名一致,是否就见名知意了呢?代码如下:
public class Student {
private String name;
private int age;
public void setName(String name) {
name = name;
}
public void setAge(int age) {
age = age;
}
}
经过修改和测试,我们发现新的问题,成员变量赋值失败了。也就是说,在修改了setXxx()
的形参变量名后,方法并没有给成员变量赋值!这是由于形参变量名与成员变量名重名,导致成员变量名被隐藏,方法中的变量名,无法访问到成员变量,从而赋值失败。所以,我们只能使用this关键字,来解决这个重名问题。
this的含义
-
this代表所在类的当前对象的引用(地址值),即对象自己的引用。
-
方法被哪个对象调用,方法中的this就代表那个对象。即谁在调用,this就代表谁。
使用场景
-
可以调用类的属性、方法和构造方法
-
当在方法内需要用到调用该方法的对象时,就用this。具体的:我们可以用this来区分属性和局部变量。 比如:this.name = name;
语法格式
this.成员变量名;
this.成员方法名();
使用this
修饰方法中的变量,解决成员变量被隐藏的问题,代码如下:
public class Student {
private String name;
private int age;
public void setName(String name) {
//name = name;
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) {
//age = age;
this.age = age;
}
public int getAge() {
return age;
}
}
方法中只有一个变量名时,默认也是使用 this 修饰,可以省略不写。
this内存解析
事实上,在jvm编译时,对于对象调用的方法,会自动在参数表中传入一个参数:this,这种参数叫做隐式参数。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xEHcsDX1-1605359763272)(./images/this.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ERorn79J-1605359763273)(C:\Users\小管同学\Desktop\东软资料\第2章-面向对象-8.13\资料\1 课件\assets\this.png)]
this关键字小结
- this关键字在普通方法里加和不加含义相同。
- 在普通方法中,this总是指向调用该方法的对象。
- 构造方法中,this总是指向正要初始化的对象。
- 构造方法调用构造方法可通过this(),可传参数,但必须位于首行。
- this不能用于static方法,static中没有对象。
面试题:
为什么在静态中不能访问非静态的的内容?
答:因为在静态结构中没有this关键字;
3.4 构造方法-构造器
构造方法,当一个对象被创建时候,构造方法用来初始化该对象,给对象的成员变量赋初始值。
构造方法的特征
-
它具有与类相同的名称
-
它不声明返回值类型。(与声明为void不同)
-
不能被static、final、synchronized(同步)、abstract(抽象)、native修饰,不能有return语句返回值
构造器的作用
-
创建对象(负责在堆中开辟确定的内存空间); 在new对象的时候,就相当于调用了构造器。
-
给对象进行初始化
语法格式
修饰符 构造方法名(参数列表){
// 方法体
}
new Person();
示例代码
public class Animal {
private int legs;
// 构造方法
public Animal() {
legs = 4;
}
public void setLegs(int i) {
legs = i;
}
public int getLegs() {
return legs;
}
}
注意事项
-
如果你不提供构造方法,系统会给出无参数构造方法。一旦自定义了构造器,那么Java不再提供。
-
如果你提供了构造方法,系统将不再提供无参数构造方法。
-
构造方法是可以重载的,既可以定义参数,也可以不定义参数。
-
构造方法可以使用
this
关键字进行调用,语法为this()
。 -
构造器的调用只能在首行
1.构造器优于成员方法被调用;
2.如果想在构造器中调用其他构造器,需要使用this关键字;
3.如果调用了其他构造器,那么这条语句必须写在第一行;
eg:
student(String name){this.name=name;}
student(int age){this(name);this age=age;}
4.不可以连续调用两个构造器;
5.为了防止构造器相互的调用,至少有一个构造器不去调用其他构造器;
-
默认初始化》显示初始化》代码块初始化》构造器》set赋值
课堂练习
(1)定义Student类,有4个属性:
String name;
int age;
String school;
String major;
(2)定义Student类的3个构造方法:
第一个构造方法Student(String name, int age)设置类的name和age属性;
第二个构造方法Student(String name, int age, String school)设置类的name, age 和school属性;
第三个构造方法Student(String name, int age, String school, String major)设置类的name, age ,school和major属性;
(3)在main方法中分别调用不同的构造方法创建的对象,并输出其属性值。
4 package和import
4.1 package
包(package)的作用
- 包帮助管理大型软件系统:将功能相近的类划分到同一个包中。比如:MVC的设计模式
- 包可以包含类和子包,划分项目层次,便于管理
- 解决类命名冲突的问题
- 控制访问权限(包访问权限–缺省)
- 包在物理形式上来看就是文件夹
包的使用
package语句作为Java源文件的第一条语句,指明该文件中定义的类所在的包。(若缺省该语句,则指定为无名包)。它的格式为:
package 顶层包名.子包名;
代码示例
package pack1.pack2; //指定类PackageTest属于包pack1.pack2
public class PackageTest{
public void display(){
System.out.println("in method display()");
}
}
注意事项
-
包对应于文件系统的目录,package语句中,用“.” 来指明包(目录)的层次;
-
包通常用小写单词标识。通常使用所在公司域名的倒置:
com.neuedu.xxx
4.2 import
为使用定义在不同包中的Java类,需用import语句来引入指定包层次下所需要的类或全部类(.*)。
import语句告诉编译器到哪里去寻找类。
语法格式
import 包名.类名;
示例代码
import pack1.pack2.Test; //import pack1.pack2.*;表示引入pack1.pack2包中的所有结构
public class PackTest{
public static void main(String args[]){
Test t = new Test(); //Test类在pack1.pack2包中定义
t.display();
}
}
注意事项
-
在源文件中使用import显式的导入指定包下的类或接口
-
声明在包的声明和类的声明之间。
-
如果需要导入多个类或接口,那么就并列显式多个import语句即可举例:可以使用java.util.*的方式,一次性导入util包下所有的类或接口。
-
如果导入的类或接口是java.lang包下的,或者是当前包下的,则可以省略此import语句。
-
如果在代码中使用不同包下的同名的类。那么就需要使用类的全类名的方式指明调用的是哪个类。
-
如果已经导入java.a包下的类。那么如果需要使用a包的子包下的类的话,仍然需要导入。
-
import static组合的使用:调用指定类或接口下的静态的属性或方法