一.面向对象的介绍
1.1简介
面向对象(Object Oriented)是一种更加优秀的程序设计方法。它由面向对象分析(OOA),面向对象设计(OOD),面向对象编程(OOP)三部分组成。
它的基本思想是使用类,对象,继承,封装,多态等进行程序设计。它从现实世界中客观存在的事物出发来构造软件系统,在系统构造中尽可能的运用人类的自然思维方式,强调直接以现实世界中的事物为中心来思考问题,认识问题。并根据这些事物的本质特点,把他们抽象地表示为系统中的类,作为系统的基本构成单元,使得软件系统的组件可以直接映射现实世界,并保持客观世界事务及其相互关系的本来面貌。
我们主要学习的就是面向对象的编程,简称OOP
1.2面向对象的类
在面向对象编程中,最小的程序单元是类,类可以生成系统中的多个对象,这些对象能够映射出现实生活中的各个实例;
在类中我们可以定义很多成员变量(属性),比如设置一个person类,成员变量就会有身高,体重,姓名,性别,等等,我们只需要记录有关于业务的成员变量就可以了;
为了操作这些成员变量,我们使用方法来将成员变量实现:
类的定义 = 成员变量 + 方法
面向对象比面向过程的粒度要大;面向对象的单位是类,而面向过程的单位是函数,因此面向对象要比面向过程简单易用,举一个例子: 在组装电脑时,用主板,CPU,内存条硬盘这样的大件组装和使用一堆三极管,二极管,集成电路等组装,哪个更简单显而易见
1.3案例比较
①把大象放进冰箱
面向过程:
第一步:打开冰箱门
第二步:把大象放进去
第三步:关上冰箱门
这些都是人的行为,需要设计不同的函数来实现这些问题
面向对象:
第一步:冰箱开门 冰箱的行为
第二步:大象进入冰箱 大象的行为
第三步:冰箱关门 冰箱的行为
②猪八戒吃西瓜
面向过程:以函数为中心,所以需要表示为:吃这个函数,变量是猪八戒和西瓜
吃(猪八戒,西瓜)
面向过程,以对象为中心,所以可以表示为:猪八戒这个类调用吃这个方法
猪八戒.吃(西瓜)
说的大白话一点,开发者想直接模拟客观世界。定义一个类,对应客观世界的一种事物;业务需要关心这个事物的哪些状态数据,就定义为成员变量;业务需要关心这个事物的哪些行为,程序就可以定义为方法
面向对象是不脱离面向过程的思想,面向对象中的方法(Method)逻辑依然是面向过程的逻辑可以说面向过程是面向对象的基础思想;
面向对象和面向过程最终的目的都是解决问题,面向过程要求的是整个解决问题的步骤,而面向对象只需要关系问题中涉及到的类和方法就可以了。方法的内部还是会有面向过程的思想,只是思维方式不一样而已。
总结:
面向对象秉承着一切都是对象的思想,关心的是这个对象的行为,对象的成员变量(属性),
面向过程关心的是每一个步骤,需要有前因,有后果
二.类与对象
2.1类的设计
[访问权限修饰符] class 类名{
成员变量
方法
}
在一个.java文件中,定义一个普通类,访问权限修饰词只能是public或者默认的
类名需要使用大驼峰命名法
成员变量需要声明
方法用来描述对象的共同行为,或者用来操作成员变量
public class Computer {
public String model;
public String color;
public String brand;
public String size;
我们定义了一个Computer类,属性有model,color,brand,size。
2.2 对象
类是对象的模板,对象是类的具体
我们可以使用new关键字来实例化对象;
使用 类名 对象名 = new 类名 ();的格式就可以创建对象了;
Computer c1 = new Computer();
我们通过上述的方法就创建出了一个Computer类型的对象c1, 引用类型声明的变量,称之为引用变量,简称引用。 因为里面存储的不是对象,而是对象的内存地址 ;引用变量,说的其实就是可以通过自己存储的地址找到哪个对象。替代了c语言里的指针。
我们如果此时使用打印语句,并不能打印出对象的属性,只能打出对象的内存地址
会得到类全名+@+16进制储存地址
2.3成员访问
①基本成员的访问
成员访问,指的就是如何使用类里的属性和方法;
通常使用点语法,对象名.成员变量 = ;
访问方法 对象名.方法名( )
public class Person {
//成员变量: 这类事物的共同特征,即状态数据,一堆不同类型的变量,也可以叫属性,全局变量
String name;
int age;
char gender;
//成员方法: 这类事物的共同行为,在这里可以直接操作成员变量
public void eat(String food){
System.out.println(name+"吃的食物是"+food);
}
public void play(String game){
System.out.println(name+"喜欢玩"+game);
}
public void work(){
System.out.println(name+"喜欢工作");
}
在上述代码中,我们创建了一个Person的类,属性有name,age,gender
成员方法有eat,play,work,在方法中,设置了形参
public class PersonTest1 {
public static void main(String[] args) {
//创建Person类型的一个对象(引用变量)
Person p1 = new Person();
//注意:成员变量都是有默认值的,在没赋值之前。
System.out.println(p1.name); // 引用类型的默认值null
System.out.println(p1.age); // byte,short,int,long的默认值是0
System.out.println(p1.gender); // char的默认值 \u0000
// // 给对象的属性赋值。
p1.name = "michael";
p1.age = 18;
p1.gender = '男';
在上述代码中,我们设置了一个测试类,提供了main方法,创建了Person类型的一个变量p1,p1内部有成员变量,初始值都是默认值,引用类型的默认值都是null,基本数据类型的默认值是0或0.0,false,\u0000
在执行打印语句打印其各个成员变量时,返回的就都是默认值;
但是我们可以利用对象名.成员变量的方式给各个成员变量赋值;
此时再打印p1的各个属性就有值了;
我们在访问成员方法时也可以使用对象名.成员方法的方式
p1.eat("蛋糕");
p1.play("王者荣耀");
p1.work();
//int...a 属于可变长参数, 变量类型...变量名 注意:该变量是数组 可变长参数只能放到最后面
public long calculate(long b,int...a){
int sum = 0;
for (int i = 0; i < a.length; i++) {
sum += a[i];
}
return sum;
}
这是在Person类中设置的另一种方法,返回值类型是long,参数列表中由两部分组成,一部分是long类型的参数b,另一部分是可变长参数,需要放在最后面;初始化sum是0,然后把可变长参数中数组的每一项都相加;返回sum;在调用时,需要long类型的接受变量,还需要输入两部分参数,一部分时long类型的参数,另一部分是可变长参数
long sum = p1.calculate(3,1,2,3,4,5);
System.out.println(sum);
最后sum的结果就是可变长参数相加之和
2.4 this关键字
this.是隐藏在成员方法中成员变量前面的关键字
哪个对象调用这个方法,this就是谁;
public class Cat {
String name;
String brand;
String color;
public void setInfo(String a ,String b ,String c){
//成员变量前隐藏了this. 可以写出来,也可以称略不写
this.name = a;
this.brand = b;
this.color = c;
}
根据上述代码,我们建立了一个Cat类,设置了三个属性分别是name,brand,color,并在下面定义了一个成员方法,setInfo,形参列表中分别是三个字符串类型的参数a,b,c.
this.name的作用就是确定把形参赋值给成员变量name,因为形参列表中的参数名和成员变量的名字不同,所以可以不加this.关键字,但是如果形参列表中的名字与成员变量相同,就需要添加this.关键字了
public void setInformation(String name ,String brand,String color){
//当形式参数的变量与成员变量的名字相同时,如果想要在这个作用于内
//表示成员变量,那么this.不能省略
this.name = name;
this.brand = brand;
this.color = color;
}
此时this.关键字不能省略;
public void showInfo(){
System.out.println("name:"+name+"brand:"+brand+"color:"+color);
}
再定义一个打印方法,来把对象中的成员变量能打印出来;
public static void main(String[] args) {
//创建cat类的对象c1;
Cat c1 = new Cat();
//调用setInfo给对象c1的各个属性赋值
c1.setInfo("小白","中国田园","白色");
//调用showInfo方法
c1.showInfo();
使用main方法,创建一个Cat类型的对象c1,并使用setInfo方法给c1的属性赋值;分别是小白,中华田园,白色,然后调用showInfo方法打印出运行结果
//调用setInformation修改属性信息
c1.setInformation("小黑","吉林品牌","黑色");
c1.showInfo();
我们还可以调用setInformation方法来修改c1对象的属性;然后打印出来
三.构造方法
3.1介绍
构造方法也是一种方法,只是和普通的方法有区别:
语法不同,构造方法没有返回值,这里说的没有返回值不是指返回值类型是void,而是返回值类型这个位置不存在;构造方法的名字,必须与类名保持一致;构造方法不能使用static修饰
执行时机不同,普通的方法,可以随时调用,但是构造方法是在实例化对象时才会调用,需要使用new 关键字来调用;
构造方法可以重载 参数列表不同即可, 如果程序员在定义某个类时,没有提供任何的构造器,系统会默认提供一个无参构造器,一旦程序员提供了某个构造器,无参构造器就不存在了,如果想使用无参构造器,需要程序员自己提供一个;
public class Person2 {
String name;
int age;
char gender;
//无参构造器
public Person2(){
System.out.println("---无参构造器---");
}
//全参构造器
public Person2(String name, int age, char gender) {
this.name = name;
this.age = age;
this.gender = gender;
System.out.println("---全参构造器---");
}
public Person2(String name, char gender){
this.name = name;
this.gender = gender;
this.age = 18;
System.out.println("---两个参数构造器---");
}
public void showInfo(){
System.out.println("Name: " + name);
System.out.println("Age: " + age);
System.out.println("Gender: " + gender);
}
}
根据上述代码,我们先提供了一个无参构造器,然后提供了一个全参构造器,因形参列表中的形参名与成员变量一致,所以需要使用this.关键字,之后又提供了一个showInfo方法,来打印对象的三个属性,之后我们新建一个测试类,实例化一个对象;
public class Person2Test {
public static void main(String[] args) {
//调用Person2的无参构造器初始化和实例化一个对象
Person2 p1 = new Person2();
p1.showInfo(); //因为无参构造器中没有初始化操作,因此都是默认值
//调用全参构造器实例化和初始化一个对象 ctrl+p用于查看重载的方法参数
Person2 p2 = new Person2("小明",20,'男');
p2.showInfo();
//调用两个参数的构造器,实例化和初始化一个对象
Person2 p3 = new Person2("小红",'女');
p3.showInfo();
}
}
可以看出,我们先使用无参构造器实例化了一个对象p1,他的成员变量值都是默认值
之后调用了全参构造器,需要输入三个实参,然后调用showInfo方法打印出来;
然后调用两个参数的构造器,成员变量age是设置好的18;
在调用构造器时,需要使用 类名 对象名 = new 类名( )的方式调用。
3.2构造器的调用(续)
在我们使用构造器时可以使用this(有参传参)的方式调用本类中的其他构造器
需要注意的是,this(有参传参)必须写在构造器的首行首句;
public class Person3 {
String name;
int age;
char gender;
//无参
public Person3() {
this.name = "";
this.age = -1;
this.gender = 'f';
}
//单参
public Person3(String name) {
this.name = name;
}
public Person3(String name,int age ) {
//调用本类中的一个参数构造器
this(name);
this.age = age;
}
public Person3(String name,int age, char gender) {
//调用本类中的两个参数构造器
this(name,age);
this.gender = gender;
}
public void showInfo(){
System.out.println("Name: " + name);
System.out.println("Age: " + age);
System.out.println("Gender: " + gender);
}
首先我们定义了一个Person3的类,然后设置了其属性分别是name,age,gender
先定义一个无参构造器,初始化每个属性的值为空,-1,f ;
然后定义了一个单参构造器,形参列表时name
定义双参构造器时,可以调用单参构造器,在构造器内部的首行首句写this(name);
定义全参构造器时可以调用双参构造器,在首行首句写this(name,age);
最后在定义一个showInfo方法用于打印
public static void main(String[] args) {
//调用无参构造器
Person3 p0 = new Person3();
p0.showInfo();
//调用两个参数构造器
Person3 p1 = new Person3("小黑",18);
p1.showInfo();
//调用全参构造器
Person3 p2 = new Person3("张三",29,'男');
p2.showInfo();
}
}
我们可以在main方法里利用无参构造器,双参构造器,全参构造器分别实例化一个对象并打印结果:
3.3代码块的学习
构造代码块:
也叫动态代码块,位置:与构造器并列都是在类体中;
特点:构造器执行一次,动态代码块就会执行一次,并且先于构造器执行,
用法:一般用于统计一个类创建了多少个对象,或者提前给成员变量赋值;
静态代码块:
需要前缀修饰词static,位置:与构造器并列,都是在类体中,
特点:只执行一次,在类加载期间就将该类的信息加载到内存
作用:一般用于加载静态资源图片,音频,视频到内存中
int a;
int b;
static {
System.out.println("---静态块执行了---");
}
{
System.out.println("---动态块执行了---");
a = 100;
b = 200;
}
public BlockDemo() {
System.out.println("---无参---");
}
public BlockDemo(int a, int b) {
this.a = a;
this.b = b;
System.out.println("---有参---");
}
/**
* String toString() :将对象的成员信息变成一串字符并返回
* 该方法,在将引用变量放在输出语句中时,会默认调用
*/
public String toString() {
return "a: " + a + ", b: " + b;
}
}
我们首先定义两个成员变量,构造一个静态块打印出,静态块执行了,再构造一个动态块打印动态块执行了,并将100赋值给a,200赋值给b,然后定义一个无参构造器,再定义一个全参构造器,之后定义一个toString( )方法,返回的结果时a和b的值
public class BlockDemo {
public static void main(String[] args) {
//调用无参构造器实例化对象
BlockDemo bd = new BlockDemo();
System.out.println(bd.toString());
//调用有参构造器实例化对象
BlockDemo bd2 = new BlockDemo(10,20);
System.out.println(bd2); //默认调用了toString()方法
}
在上述代码中,我们使用main方法,调用无参构造器实例化对象,然后通过对象调用toString方法
之后使用有参构造器实例化一个对象bd2,打印时会默认调用toString方法;
通过运行结果我们可以发现,静态块是在类加载期间运行的,所以静态块执行的最早,其次是我们在调用无参构造器时,动态块会先于构造器执行,每次在实例化一个对象时,动态块都会执行一次