面向对象编程基础
- 面向对象 vs 面向过程
- 类与对象
- 属性与方法
- 属性
- 方法
- 对象的内存解析
- 匿名对象
- 再谈方法
- 方法的重载
- 可变形参
- 值传递
- 对象的关联
- 四种权限修饰符
- 对象的三大特性之封装性
- 构造器
- 属性的赋值方式与顺序
- JavaBean
面向对象 vs 面向过程
在现实生活中,当我们要吃饭的时候通常有两种解决的方式:
面向过程:自己做饭吃,洗菜->淘米->切菜->炒菜->盛盘…,这时我们所要考虑的是做饭的流程,这就是面向过程的基本思想
面向过程:聘请一位厨师告诉厨师我要吃什么,让厨师去做,这时我们所要考虑的就是我要聘请哪个厨师,而不是如何去做,这就是面向对象的基本思想
类与对象
类是对一类事物的描述,是抽象的、概念上的定义
对象是实际存在的该类事物的每个个体,因而也称为实例
通俗的说,类与对象的关系其实就相当于手机设计图与手机的关系,类就相当于手机设计图,而对象就是按照手机设计图设计出来的手机
因此我们可以说对象是按照类创造出来的实例
那么,如果构建一个类呢?其实非常简单,在java中我们使用class
+类名{}表示构建一个类,构建完类,我们可以根据类new
出来一个对象,创建方式:类名 对象名 = new
类名();
演示:
class Student{
}
public class Test{
public static void main(String[] args){
Student s = new Student();
}
}
属性与方法
属性是指一个类所描述的名词特征,比如姓名,性别,年龄。。。而方法表示的则为动词特征,比如跑,说话,吃饭。。。
说完两者之间的区别,我们开始说属性和方法的声明和使用
属性
- 局部变量 vs 成员变量(属性)
- 共同点:
- 都是先声明后使用
- 两者的声明方式一样
- 两者都有作用域
- 不同点:
- 声明的地方不一样,局部变量声明在方法等结构内(方法体,方法的形参,构造器内,构造器的形参,代码块),而属性声明在方法等结构外
- 属性有权限修饰符,局部变量没有
- 属性有默认值,局部变量没有(因此局部变量要初始化后才能使用)
- 局部变量在内存的栈中,而属性在内存的堆中的对象内
- 属性的声明方式
权限修饰符(private,缺省的,protected,public) 属性的数据结构 属性名; - 属性的调用
对象名.属性名即可调用
class Student{
private String name;
private int age;
}
public class Test{
public static void main(String[] args){
Student s = new Student();
s.name = "云茧";
s.age = 18;
}
}
方法
- 方法的声明
权限修饰符 返回值类型 方法名(形参列表){
方法体;
}
- 权限修饰符:private,缺省的,protected,public
- 返回值类型:无返回值(void)与有返回值(基本数据结构返回值/引用数据结构返回值),有返回值时在方法体必须有return
- 方法名:遵守命名规则与规范即可
- 形参列表:可以是0个 1个或者多个形参,但在声明中有形参是在调用该方法时必须传入与形参对应数据类型的实参
- 方法体:方法具体功能的实现部分
演示:
class Student{
private String name;
private int age;
public void show(){
System.out.println("我叫" + name + ",我" + age + "岁了");
}
}
public class Test{
public static void main(String[] args){
Student s = new Student();
s.name = "云茧";
s.age = 18;
s.show();
}
}
运行结果:我叫云茧,我18岁了
方法只有4中形式:
- 无返回值无参
- 有返回值无参
- 有返回值有参
- 无返回值有参
因此在创建一个方法时,一定要思考两个问题:
- 要不要返回值?
- 要不要形参 ?
在相同的类中各方法可以互相调用,但是除递归外不能自己调用自己,否则会报错
- return关键字:当返回值类型为void时,return的功能为结束方法,当有返回值时,return功能为返回return后面的值,且return必须执行
对象的内存解析
匿名对象
匿名对象顾名思义就是没有名字的对象,它的声明方式也是非常简单,new 类名
即可,那么思考以下代码运行的结果
class Computer{
String type;
}
public class Test{
public static void main(String[] args){
new Computer().type = "戴尔";
System.out.println(new Computer().type);
}
}
运行结果:null
这个结果就反映了匿名对象的一个特点,只能被调用一次,既然只能被调用一次,那么匿名对象到底有什么作用呢?当实参传入方法
演示一下
class Computer{
String type;
}
public class Test{
public static void main(String[] args){
Test t = new Test();
t.setComputer(new Computer());
}
public void setComputer(Computer c){
}
}
再谈方法
方法的重载
需求:分别编写方法实现两个int值相加,两个double值相加,两个char值相加。。。
要实现上面的需求,我们在给方法名取名的时候可能要取三个甚至更多的方法名,这给我们编写与使用方法造成了诸多的不变,那么有办法解决这个问题吗?
答案是肯定的,比如我们的System.out.println()
方法好像既可以传入int类型的值,又可以传入double类型的值,而像println
这种相同的方法名不同参数的这种技术就叫做方法的重载
演示
public class test{
public void add(int a,int b){
a+b;
}
public void add(double a,double b){
a+b;
}
}
以上两个add方法就是方法的重载
简单来说,方法重载就是在相同的类中,具有相同方法名不同形参列表的对象
而不同形参列表具体是指形参的个数,类型,顺序不同
注:形参名不同,返回值不同,权限修饰符不同不能构成方法重载
可变形参
需求:编写方法计算任意int型数值的和
面对上面的需求,我们的第一个思路应该就是将形参定义成一个int类型的数组
,没错,这样确实可以实现这个需求,但是java还给我们提供了其他的解决方式,这个解决方式就是可变形参
定义方式数据类型 ... 形参名
可变形参可以传入任意个数该数据类型的变量也可以传入该数据类型的数组
因为可变形参
的底层是数组
,因此在方法中的使用方式和数组一样
演示:
public class Test{
public static void main(String[] args){
Test t = new Test();
int sum = t.add(1,2,3,4);
System.out.println(sum);
}
public int add(int ... num){
int sum = 0;
for(int i = 0;i < num.length;i++){
sum += num[i];
}
return sum;
}
}
运行结果:10
注:形参列表中可变形参只能有一个,并且只能放在形参列表的最后一个
值传递
需求:编写方法实现两个int变量的数据交换
public class ValueTransfor {
public static void main(String[] args) {
m = 5;
n = 10;
vt.SwapData(m,n);
System.out.println("m:" + m + " n:" + n);
}
public void SwapData(int m,int n){
System.out.println("SD前:m:" + m + " n:" + n);
int temp = m;
m = n;
n = temp;
System.out.println("SD后:m:" + m + " n:" + n);
}
}
运行结果
SD前:m:5 n:10
SD后:m:10 n:5
m:5 n:10
在运行结果中我们可以看到在方法中我们实现了m和n的数据交换,但实际上m和n并没有交换,这是为什么呢?
这就涉及到了java中方法参数传递的机制—值传递
即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响。
这就像我复印了一份资料给你,你在你的资料上进行修改,不会对我的资料造成影响
代码内存解析图:
在考虑以下代码的运行结果:
class Number{
int m;//为了方便先不考虑封装性
int n;
}
public class ValueTransfor {
public static void main(String[] args) {
ValueTransfor vt = new ValueTransfor();
Number number = new Number();
number.m = 5;
number.n = 10;
vt.SwapData(number);
System.out.println("m:" + number.m + " n:" + number.n);
}
public void SwapData(Number number){
System.out.println("SD前:m:" + number.m + " n:" + number.n);
int temp = number.m;
number.m = number.n;
number.n = temp;
System.out.println("SD后:m:" + number.m + " n:" + number.n);
}
}
运行结果
SD前:m:5 n:10
SD后:m:10 n:5
m:10 n:5
为什么这回就能交换成功呢?
让我们看一下该代码的内存解析图:
这个可以理解为我把我的房间的钥匙又配了一把给你,你拿钥匙进入我的房间进行修改,会对我的房间造成影响
总结一下:
形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参
形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参
对象的关联
当一个对象中的属性存放了另一个对象的地址时我们就说这两个对象是关联的
演示
class Computer{
String type;
CPU cpu;
}
class CPU{
String type;
}
public class ObjectConnectTest {
public static void main(String[] args) {
Computer computer = new Computer();
computer.type = "戴尔";
CPU cpu = new CPU();
cpu.type = "i7";
computer.cpu = cpu;
}
}
这时我们就说Computer对象与CPU对象是关联的
其内存解析图如下:
四种权限修饰符
对象的三大特性之封装性
封装性是指把对象中的属性隐藏,不让外界直接用对象名.属性名访问,而是通过对象中构造get/set
方法进行间接访问
为什么要封装?
思考一个问题,如果我们构造一个类中有一个age
属性,我们创建对象后,用对象名.属性名的方式给age
属性赋值为-1,这显然是不合理的,但是在没有封装前的属性只能限制属性的类型,但我们可以不让外界直接访问属性,而是依靠对象的一个方法类为属性赋值,这样我们就可以在方法内对属性进行限制
get/set方法
在Java中,我们通常把取出属性值的方法命名为getXxx,把为属性赋值的方法命名为setXxx。
关键字this的用法:
在Java中,this指当前对象,简单来说,就是对象调用方法中哪个对象调用的方法,在方法中的this就是指哪个对象,如果在方法中调用属性,可以省略this,但是当局部变量与方法名一致时必须用this.属性名来表示属性
用法:
- 用
private
修饰属性 - 为属性提供相应的
get/set
方法
演示:
class Person{
private String name;
private int age;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public int getAge(){
return age;
}
public void setAge(int age){
if(age < 0){
System.out.println("年龄不合法,赋予默认值为25");
this.age = 25;
}else{
this.age = age;
}
}
public void show(){
System.out.println("name:" + name + " age:" + age);
}
}
public class Test{
public static void main(String[] args){
Person p = new Person();
p.setName("云茧");
p.setAge(18);
p.show();
}
}
运行结果:name:云茧 age:18
构造器
什么是构造器?
构造器是一种方法名与类名一致,且不声明返回值(不是void)的一种特殊的方法,也被称为构造方法
声明方式:
权限修饰符 类名(形参列表){
初始化语句
}
构造器的作用:
- 创建对象
- 为对象中属性进行初始化
- 执行在对象中只用一次的代码
特性:
- 当类中没有显示的声明一个构造器时,Java底层会自动生成一个空参的构造器,但一旦显示声明了一个构造器,空参构造器就不会被自动生成
- 构造器之间会形成方法的重载
- 在同一个对象中每个构造器只执行一次
this在构造器中的使用:
在构造其中可以用this(形参列表)
,调用其他构造器
注:this(形参列表)在构造器中,只能放在第一行代码中,并且如果有n个构造器,最多就只能有(n-1)个this()方法
构造器演示:
class Person(){
private String name;
private int age;
public Person(){}
public Person(String name,int age){
this.name = name;
this.age = age;
}
public void show(){
System.out.println("name:" + name + " age:" + age);
}
}
public class Test{
public static void main(String[] args){
Person p = new Person("云茧",18);
p.show();
}
}
运行结果:name:云茧 age:18
属性的赋值方式与顺序
默认值 --> 显示赋值 --> 构造器赋值 --> get/set方法赋值
JavaBean
在项目中,会有大量各式各样的数据会从前端和数据库,传入我们的后台Java程序中,不管任何类型的数据只要传入后台都会被封装成一个对象进行数据的储存(万物皆对象),而构建这些对象的类就叫做JavaBean
特点:
- 权限是public(公共的)
- 有一个无参的公共构造器
- 有属性,并有相应的get/set方法