面向对象基础
1.概念
1.1 面向对象编程的定义和与面向过程编程的区别
- 面向对象编程(Object Oriented Programming)
是一种以对象为中心的编程思想(目前自己觉得像是定义方法然后调用)
- 面向过程编程(Procedure Oriented Programming)
是一种以过程为中心的编程思想,学习此章节之前的编程都是使用此思想,即所有要实现的需求都是一步步自己写的代码来实现
1.2 注意事项
- 客观存在的任何一种事务都可以看作程序中的对象
- 使用面向对象思想可以将复杂的问题简单化
- 将我们从一行行敲代码的执行者位置变成了指挥对象执行命令的指挥者
2.类和对象
2.1 类和对象的概念
类是对现实生活中一类具有共同属性和行为的事物的抽象,类是对对象的抽象
对象是能够看得到摸得着的真实存在的实体,对象是对类的实例化
类是对事物,也就是对象的一种描述,可以将类理解为一张设计图,根据设计图可以创建出具体的事物,也就是对象
2.2 类的组成
- 属性
该事物的各种特征,在代码中通过成员变量来体现,类中方法外的变量叫做成员变量
成员变量和局部变量的区别:
区别 | 成员变量 | 局部变量 |
---|---|---|
类中的位置不同 | 类中方法外 | 类中方法内(包括形式参数) |
内存中位置不同 | 堆内存 | 栈内存 |
生命周期不同 | 随着对象存在而存在,随着对象想消失而消失(在内存中找不到该对象后Java的垃圾回收器会自动清理掉对象开辟的空间) | 随着方法的调用而存在,随着方法的调用完毕而消失 |
初始值不同 | 有默认的初始化值 | 没有默认的初始化值,必须先定义,赋值,才能使用 |
- 行为
该事物存在的功能(能做的事情),在代码中通过成员方法来体现,去掉static关键字的方法就叫做成员方法
2.3 类的定义
2.3.1 格式
public class 类名{
变量1的数据类型 变量1名;
变量2的数据类型 变量2名;
方法1;
方法2
}
2.3 对象的使用和创建
2.3.1 对象的创建
格式:
类名 对象名 = new 类名();//创建一个对象类型的变量,对象类型的变量属于引用类型的变量
2.3.2 对象的使用
- 使用成员变量
格式:
对象名.变量名
- 使用成员方法
格式:
对象名.方法名();
2.3.3 对象使用的注意事项
- 直接使用成员变量打印出来时类中成员变量的默认初始化值值
- 成员变量定义时可以不初始化,会有系统自动赋值默认值
- 对象创建后会在内存中开辟空间,地址值为:全类名+@+十六进制地址值
- 同一个类的对象,成员变量互不影响,堆内存记录的成员方法在方法区的内存值相同
- 示例:
package com.huly.contrast;
public class moopDemo {
int num;
String name;
public void student(){
System.out.println("学生");
}
}
package com.huly.contrast;
public class testMoop {
public static void main(String[] args) {
moopDemo stu = new moopDemo();//创建对象
System.out.println(stu);
//打印结果:com.huly.contrast.moopDemo@1b6d3586
//打印对象的地址值,创建对象后会在内存中开辟空间
//@前面是全类名(包名+类名),后面是十六进制地址值
System.out.println(stu.num);//打印成员变量:0;int型变量默认初始化值为零
System.out.println(stu.name);//打印成员变量:null;字符串型函数默认初始化值为null
//成员变量定义时可以不初始化,会有系统自动赋值默认值
stu.num=1;//将1赋值给成员变量
stu.name="azhen";//将azhen赋值给成员变量name
System.out.println(stu.num+"···"+stu.name);//打印成员变量:1···azhen
stu.student();//调用类中的方法
moopDemo stu2 = new moopDemo();//创建第二个对象
System.out.println(stu2);
//打印结果:com.huly.contrast.moopDemo@4554617c
System.out.println(stu2.num+"---"+stu2.name);
/*
打印结果:0---num,此结果说明同一个类创建的对象会在堆内存中开辟一块新空间
对象stu已经将成员变量赋值,此处还是默认值,说明成员变量在不同的对象中相互独立
*/
stu2.num = 2;
stu2.name = "aqiang";
System.out.println(stu2.num+"---"+stu2.name);//打印结果:2---aqiang
stu2.student();
//打印结果为:学生;此结果说明对象记录的方法地址值一样,是一个方法
}
}
- 示例内存讲解
1.上述程序有两个类,在程序运行时mian函数作为程序的入口,其所在类的字节码文件会首先加载到内存的方法区,类里的代码随着类也一起到了方法区,注意:一个程序只能有一个主方法,主方法是用来测试用的
2.主方法在类的字节码文件中,被自动调入栈内存中从上到下依次执行
3.执行main的第一条语句:moopDemo stu = new moopDemo();
创建一个对象stu,因为在内存中还没有moopDemo的类,所以在创建对象之前会将moopdemo这个类的字节码文件加载进方法区(类中的成员变量和成员方法会跟着一起进入方法区),然后new关键字会在堆内存中开辟一个空间并产生地址,且此地址被赋值给了stu,空间用来放成员变量和成员方法的地址,其中堆内存中的变量会被赋予默认初始化值,int型变量默认初始化值是0,string型变量默认初始化值是null。
3.1 System.out.println(stu); 打印对象stu记录的地址值
4.System.out.println(stu.num); System.out.println(stu.name);这两条语句会打印默认的成员变量值。现根据stu记录的地址值找到moopDemo在堆内存中的空间,然后根据成员变量名找到其变量记录的值
5.stu.num=1;stu.name="azhen";System.out.println(stu.num+"···"+stu.name);替换掉成员变量num和name的初始值,并打印
6.stu.student();通过stu记录的地址找到堆内存中的空间,然后在空间中找到student的地址值,根据地址值找到在方法区中moopDemo字节码文件的student方法,然后该方法会被加载到栈内存中执行
7.因为student方法中只有一条语句,所以将此语句输出之后,student方法会在内存中释放
8. moopDemo stu2 = new moopDemo(); System.out.println(stu2);
创建第二个对象,在堆内存中重新开辟一个空间,因为moopDemo已经加载进方法区了,所以无需再次加载,并打印地址值:com.huly.contrast.moopDemo@4554617c;
此地址和第一个对象记录的地址值不同,说明是两个不同的空间。
9.执行:System.out.println(stu2.num+"---"+stu2.name);执行结果:0---null;说明新声明的对象中的成员变量不受其他对象的影响
10.执行:stu2.student();输出结果:学生;此处说明两个对象记录的方法的地址相同,所以调用成员方法得到了一样的结果,此处执行时会根据stu2记录的地址值找到在堆内存中开辟的空间,然后根据控件中记录的方法的地址,找到方法区中的方法,并加载进栈内存中,在栈内存中将语句执行完毕之后弹栈消失,此条语句是main方法的最后一条语句,所以执行完毕之后,main也会从栈内存中释放,此程序即执行完毕
3 封装
3.1 private 关键字
- private是一个权限修饰符
- 可以修饰成员(成员方法和成员变量)
- 被private修饰的成员只能在本类中才能访问
注意:之前学的public关键字也是权限修饰符,被public修饰的成员在哪都可以被访问(隔着项目需要配置CLASSTAPH)
3.1.1 使用方法示例
package com.huly.object;
public class student {
private int age;
String name;
public void setAge(int a){
if(a<120&&a>0){
age = a;
}
}
public int getAge(){
return age;
}
public void student(){
System.out.println(name+"---"+age);
}
}
package com.huly.object;
import com.sun.corba.se.impl.encoding.CDROutputObject;
public class test01 {
public static void main(String[] args) {
student stu = new student();
stu.student();
stu.name = "azhen";
stu.setAge(-23);
stu.student();
stu.setAge(23);
stu.student();
System.out.println(stu.getAge());
}
}
3.2 this关键字
-
this 关键字是一个标识符,用来标识成员变量
-
代表所在类的对象引用,方法被哪个对象调用,this就代表哪个对象
3.2.1 this关键字的用法
- 格式
this.成员变量名
- 可以调用本类的成员(变量、方法),解决局部变量和成员变量的重名问题
Java中变量的使用采取就近原则,同一个变量名,成员变量和局部变量哪个离使用变量的表达式越近,使用的就是哪个变量
package com.huly.mthis;
public class mthis {
private int age;
public void setAge(int age){ //假设此处形参调用的值为30
/*age = age;
此处两个age都为局部形式变量,在主方法调用赋值后对成员变量age无影响,所以后续show方法中打印结果为:0
*/
this.age = age+1;
/*
前一个age被this关键字标识,意味着此处的age为成员变量,后一个age因为没有标识符,根据就近原则为局部变量,此处运行之后局部形式变量age获取的数据会赋值给成员变量age
*/
System.out.println(age);
//根据就近原则,此条表达式离局部变量更近,所以此处为打印的值为局部变量的值30
System.out.println(this.age);
//此处age前面有成员变量的标识符,所以打印结果为31
}
public int getAge(){
return age;//return 返回的值默认为成员变量,即是return this age;
}
public void show(){
System.out.println(age);
//此处打印的是成员变量的值,成员变量可以在整个类中被调用,局部变量只能在相应方法内被调用
}
}
- this代表所在类的对象引用,被哪个对象调用就代表哪个对象
package com.huly.mthis2;
public class ipthis {
int age;
public void setAge(int age){
this.age = age;
System.out.println(this);
}
}
package com.huly.mthis2;
public class test1 {
public static void main(String[] arge){
ipthis s1 = new ipthis();
System.out.println(s1);//打印结果:com.huly.mthis2.ipthis@1b6d3586
s1.setAge(3);//运行结果:com.huly.mthis2.ipthis@1b6d3586
//this代表所在类对象的一个引用地址,即代表所在类对象的内存地址
ipthis s2 = new ipthis();
System.out.println(s2);//打印结果:com.huly.mthis2.ipthis@4554617c
s2.setAge(4);//运行结果:com.huly.mthis2.ipthis@4554617c
//方法被哪一个对象调用,就代表哪个对象的内存地址
}
}
- this内存原理
package com.huly.mthis3;
public class mythis {
private int age;
public void setAge(int age){
this.age = age;
}
public void show(){
System.out.println(this.age);
}
}
package com.huly.mthis3;
public class test {
public static void main(String[] args){
mythis s1 = new mythis();
s1.setAge(3);
s1.show();
mythis s2 = new mythis();
s1.setAge(4);
s1.show();
}
}
/*
1.程序运行后会将有主方法的类的字节码文件(.class文件)加载到方法区,虚拟机会自动将方法区的主方法加载到栈内存,方法中的语句和表达式从上到下依次运行
2.执行mythis s1 = new mythis();创建对象会将对象使用的类加载如方法区等待调用,new关键字会在堆内存中开辟空间,把地址值赋值给对象s1,并将成员变量赋于初始化值和记录成员方法的地址
3.执行s1.setAge(3);执行类中的方法会将方法从方法区中加载到栈内存中,表达式会根据s1记录的地址值找到堆内存中开辟的空间,并根据空间中记录的setAge方法的地址值找到方法区中setAge方法,然后方法进入栈内存中后将参数值3传递给方法的中的形式参数并开始执行方法中的内容
4.执行this.age = age;age形参记录的值为3,将3赋值给成员变量age;成员变量是根据this记录的地址值找到堆内存的空间,然后根据变量名找到相应成员变量,再将3的值替换掉成员变量默认的初始化值(局部变量存储在栈内存中,成员变量根据this的地址值定位到堆内存中就不会再和栈内存中相同名的局部变量重名报错了)
5.setAge方法执行完毕从栈内存中消失,继续执行主方法中的语句
6.执行s1.show();根据s1记录的地址值找到堆内存中的空间,根据show名找到空间中记录的地址值找到方法区中的show方法,并将方法加载到方法区执行
7.执行输出语句System.out.println(this.age);根据this记录的地址值找到对象指向的堆内存中的空间,并根据age变量名,找到age变量记录的数据并打印,打印之后show方法执行完毕从内存中消失,继续执行主方法
8.创建对象,因为对象使用的类的字节码文件已经加载到了方法区,所以无需从新加载,但重新开辟空间后会重新给成员变量赋予默认值,因为类在方法区的位置没变,所以s2指向的空间中记录的方法地址值仍和s1对象中记录的地址值相同
。。。。。。
n.show方法执行完毕后弹栈消失,继续执行主方法,show是主方法的最后一条语句,所以主方法也执行完毕弹栈消失,整个程序执行完毕
*/
3.3 封装
- 面向对象的三大特征之一(封装、继承、多态)
- 隐藏实现细节,仅对外暴露公共访问方式
3.3.1 封装常见的体现
a.私有成员变量,提供setXXX和getXXX方法
b.将代码抽取到方法中,这是对代码的一种封装
c.将属性抽取到类当中,这是对数据的一种封装
3.3.2 封装的优点
a.提高代码的安全性,使用private关键字使访客无法对封装的代码成员变量直接修改
b.提高了代码的复用性
3.4 构造方法
构建、创造对象所调用的方法
3.4.1 格式
public 方法名(){
方法体;
}
a.方法名与类名相同,大小写也要一致
b.没有返回值,不能由return带回结果数据
c.没有返回值类型,连void都没有
3.4.2 注意事项
1.创建对象的时候调用,每创建一次对象就会执行一次构造方法
2.不能手动调用构造方法
3.如果没有定义构造方法,系统将会给出一个默认的无参数构造方法
4.如果定义了构造方法,系统将不再提供默认的构造方法
public 方法名(){}
5.如果自定义了带参数的构造方法,还要使用无参数的构造方法就必须再写一个无参数构造方法,由于构造方法和类名相同,所以两个名字相同的构造方法构成了重载关系,推荐写代码时无论是否使用都手动书写无参数构造方法和带参数构造方法
3.4.3 构造方法的作用
本质上是用来创造对象的,可以利用构造方法的执行特性额外来执行一个给对象数据(属性)进行初始化的一个作用
package com.huly.construtor;
public class student {
int age;
String name;
public student(int age,String name){
this.age=age;
this.name=name;
}
}
package com.huly.construtor;
public class test {
public static void main(String[] args){
student s1 = new student(22,"hhh");
System.out.println(s1.age);
System.out.println(s1.name);
}
}
3.4.4 标准类示例
public class student {
private int age;
private String name;
public student(){}
public student(int age,String name){
this.age=age;
this.name=name;
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void show(){
System.out.println(age+"---"+name);
}
}