面向对象(五)
面向对象编程就是拿东西来做对应的事,就比如人要洗衣服会拿洗衣机洗。
类和对象
类:是对象共同特征的描述。
对象:是真实存在的具体东西。
在java中,必须先设计类,才能获得对象。
类是对象的抽象,对象是类的实例。
类的定义:
public class 类名{
1.成员变量(代表属性,一般是名词)
2.成员方法(代表动作,一般是动词)
3.构造器
4.代码块
5.内部类
}
类的对象
类名 对象名 = new 类名();
类的使用
访问属性:
对象名.成员变量
访问行为:
对象名.方法名(……)
定义类的注意事项
类名首字母建议大写,见名知意,驼峰模式。
一个java文件可定义多个class类,但只能一个类是public修饰,public修饰的类名必须成为代码文件名。
实际开发中建议一个文件定义一个class类。
成员变量的完整定义格式:
修饰符 数据类型 变量名称=初始值;
一般无需指定初始化值,存在默认值
用来描述一类事物的类,叫做:javabean类。在javabean类中,是不写main方法的。
编写main方法的类,叫做测试类。可以在测试类中创建javabean类的对象并进行复制调用
例子
//javabean类
//Student.java
public class Student{
String name;
String sex;
int grade;
public void play(){
System.out.println(name+"在玩");
}
public void study(){
System.out.println(name+"在学习");
}
}
//测试类
public class StudentTest{
public static void main(String[] args){
Student student=new Student();
student.name="张三";
student.sex="男";
student.grade=60;
student.play();
student.study();
}
}
封装
封装便是告诉我们,如何正确设计对象的属性和方法。
对象代表什么,就要封装相应的数据,并提供数据相应的行为。
private关键字
private是一个权限修饰符,可以修饰成员(成员变量和成员方法),被private修饰的成员只能在本类中才能访问。
针对private修饰的成员变量,若需被其他类使用,要提高响应的操作。
提供"setXXX(参数)"方法,用于给成员变量赋值,方法用public修饰。
提供"getXXX()"方法,用于获取成员变量的值,方法用public修饰。
例:
//Student.java
public class Student{
private String name;
private String sex;
private int grade;
public void setName(String n){
name=n;
}
public String getName(){
return name;
}
public void setSex(String s){
sex=s;
}
public String getSex(){
return sex;
}
public void setGrade(int g){
if(g>=0 && g<=100){
grade=g;
}else{
System.out.println("参数非法");
}
}
public int getGrade(){
return grade;
}
}
//StudentTest.java
public class StudentTest{
public static void main(String[] args){
Student student=new Student();
student.setName("zhangsan");
student.setSex("男");
student.setGrade(56);
System.out.println(student.getName());
System.out.println(student.getSex());
System.out.println(student.getGrade());
}
}
this关键字
当成员变量和局部变量重名时,就会触发就近原则(谁离我近,我就用谁)
this可以区别成员变量和局部变量。
this的本质:代表所在方法调用者的地址值。
作用:可以调用本类成员(变量,方法)。
this的省略规则:
- 本类成员方法:没有前提条件,this可以直接省略
- 本类成员变量:方法中没有出现重名变量,this可以省略
public class Student{
private int age;//成员变量
public void method(int age){
int age=10;//局部变量
System.out.println(age);
//触发就近原则,打印局部变量age,10
System.out.println(this.age);
//此时age是成员变量
}
}
例:
//Student.java
public class Student{
private String name;
private String sex;
private int grade;
public void setName(String name){
this.name=name;
}
public String getName(){
return name;
}
public void setSex(String sex){
this.sex=sex;
}
public String getSex(){
return sex;
}
public void setGrade(int grade){
if(grade>=0 && grade<=100){
this.grade=grade;
}else{
System.out.println("参数非法");
}
}
public int getGrade(){
return grade;
}
}
//StudentTest.java
public class StudentTest{
public static void main(String[] args){
Student student=new Student();
//this代表的是student的地址值
student.setName("zhangsan");
//this.name=name;
//等号右边name遵循就近原则,为形参,接收zhangsan
//等号左边this为student的地址值,取的成员变量name
student.setSex("男");
student.setGrade(56);
System.out.println(student.getName());
System.out.println(student.getSex());
System.out.println(student.getGrade());
}
}
构造方法
构造方法也就做构造器,构造函数。
作用:在创建对象时给成员变量进行赋值。
分类:无参数构造方法和有参数构造方法
格式
public class 类名{
//空参构造方法
修饰符 类名(){
方法体
}
//带全部参数构造方法
修饰符 类名(参数){
方法体;
}
}
特点
- 方法名与类名相同,大小写也一致
- 无返回值类型,也没有void
- 也不能由return带回结果数据
执行时机
- 创建对象时由虚拟机调用,不能手动调用构造方法
- 每创建一次对象,就会调用一次构造方法。
注意事项
- 构造方法的定义
- 若未定义构造方法,系统将给出一个默认的无参数构造方法
- 若定义了构造方法,系统不再给出默认的无参数构造方法
- 构造方法的重载
- 带参和无参构造方法,两者方法名相同,参数不同,称为构造方法的重载
- 无论是否使用,都尽量写无参和带参的构造方法
范例
//javabean类
//Student.java
public class Student{
private String name;
private int age;
//无参构造方法
public Student(){
}
//带参构造方法
public Student(String name,int 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;
}
}
//测试类
//StudentTest.java
public class StudentTest{
public static void main(String[] args){
Student student=new Student("zhangsan",16);
System.out.println(student.getName());
System.out.println(student.getAge());
}
}
标准的JavaBean类
- 类名需见名知意。
- 成员变量使用private修饰
- 提供至少两种构造方法
- 无参构造方法
- 带全部参数构造方法
- 成员方法
- 提供每一个成员变量的对应的setXXX()/getXXX()
- 若有其他行为,也写上
成员变量和局部变量的区别
- 类中位置不同:
- 成员变量在类中,方法外
- 局部变量在方法内,方法声明上
- 初始化值不同:
- 成员变量有默认初始化值
- 局部变量没有
- 内存位置不同:
- 成员变量在堆内存
- 局部变量在栈内存
- 生命周期不同:
- 成员变量随着对象创建而存在,随着对象消失而消失
- 局部变量随着方法的调用而存在,随着方法的运行结束而消失
- 作用域不同
- 整个类中有效
- 当前方法有效
对象内存
在创建对象时系统最少会做七件事
- 加载class文件
- 申明局部变量
- 在堆内存中开辟一个空间
- 默认初始化
- 显示初始化
- 构造方法初始化
- 将堆内存中的地址值赋值给左边的局部变量
创建一个对象
范例:
//javabean类
//Student.java
public class Student{
private String name;
private int age;
//无参构造方法
public Student(){
}
//带参构造方法
public Student(String name,int 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;
}
}
//测试类
//StudentTest.java
public class StudentTest{
public static void main(String[] args){
Student student=new Student();
}
}
字节码文件:.java源文件经过编译生成的.class便是字节码文件,但还不能被系统直接执行,通过JVM解释翻译后才可以被底层系统执行。
首先StudentTest.java的字节码文件加载到方法区,main方法在方法区临时存储。随后,虚拟机自动调用程序的主入口main方法,main方法会被压入栈内,然后利用代码 Student student=new Student(“zhangsan”,16);创建对象student,执行上述的七步,具体为:
- 加载class文件:
- 将Student.java加载到方法区,包括其中所有成员变量和方法
- 申明局部变量
- Student student=new Student();执行“=”左边的代码,在栈内存的main方法中创建变量student,student能够存储Student类对象的地址值
- 在堆内存中开辟一个空间
- Student student=new Student();执行“=”右边的代码。会将所有类中的成员变量和成员方法的地址值存储到堆内存
- 默认初始化
- 将成员变量初始化。name为null,age为0
- 显示初始化
- 若在定义成员变量时直接赋值(比如priavte String name=“zhangsan”;private int age=15;)这就叫做显示初始化,不过范例中并未初始化,所以这步可忽略
- 构造方法初始化
- 范例中是空参构造,可以忽略;若是有参构造创建对象,name和age便会被赋值
- 将堆内存中的地址值赋值给左边的局部变量
- Student student=new Student();将右边代码在堆中创建的对象的地址赋给student
注:当main方法执行完毕,便会出栈,出栈后main方法的所有变量都会被释放,其中包括存储对象地址的变量,即范例中student变量被释放,若没有变量指向对象在堆内存中所处的空间,该空间便会消失。
多个对象
范例:
//javabean类
//Student.java
public class Student{
private String name;
private int age;
//无参构造方法
public Student(){
}
//带参构造方法
public Student(String name,int 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;
}
}
//测试类
//StudentTest.java
public class StudentTest{
public static void main(String[] args){
Student student1=new Student();
Student student2=new Student();
}
}
student1创建步骤与上面创建单个对象的步骤大致相同。
student2不需要再次加载class文件,相对于student少了一步,而其他步骤与创建student1大致相同
多个引用指向同一对象
范例:
//javabean类
//Student.java
public class Student{
private String name;
private int age;
//无参构造方法
public Student(){
}
//带参构造方法
public Student(String name,int 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;
}
}
//测试类
//StudentTest.java
public class StudentTest{
public static void main(String[] args){
Student student1=new Student();
Student student2=student1;
student1.setName("ljs");
student1.setAge(15);
System.out.println(student1.getName());
System.out.println(student2.getName());
System.out.println(student1.getAge());
System.out.println(student2.getAge());
student2.setName("sjl");
student2.setAge(16);
System.out.println("==============");
System.out.println(student1.getName());
System.out.println(student2.getName());
System.out.println(student1.getAge());
System.out.println(student2.getAge());
}
}
/*
ljs
ljs
15
15
==============
sjl
sjl
16
16
*/
创建student1对象的步骤与创建单个对象的步骤大致相同,student2被赋值了student1存储的对象地址,student1和student2的存储的对象地址相同,也就是这两个变量指向同一个对象的地址,所以当其中一个对象的成员变量被改变,另一个也会跟着改变,比如student1.name改为sjl,student1.age被改为16,则student2.name和student2.age也会发生改变,变为sjl和16。
当student1和student2都不再存储对象在堆内存中所处的地址时,该对象会消失。