开头:
何谓"面向对象"
面向对象是一种编程思想。
思想是一个很虚无缥缈的东西,但是它可以从一个人的具体行动中体现出来,如果说你坚持每天跑步,每天读书,做一些有益于身心健康的事情,那么可以说你有让自己更上进的思想。(白日梦是想,不是思想)
思想是行动的原则,行动是思想的体现。
编程是一种行动,编程思想是一种思想,可以说编程的原则是编程思想。
然后最后来看"面向对象",他是一种编程思想,展开一下就是面向对象编程的原则是面向对象编程思想。
OK,现在我们只要弄清楚什么是面向对象编程就弄懂什么是面向对象编程思想了。
刚才,我们处在我们不知道自己不知道的阶段,现在,我们应该处在我们知道自己不知道的阶段,学完之后,我希望大家都处在我们不知道自己知道的阶段,或者处在我们知道自己知道的阶段。
面向对象编程基础
面向过程(POP)和面向对象(OOP)
这二者都是一种编程思想,面向对象是相对于面向过程而言的。
面向过程强调的是功能行为,以函数为最小单位,考虑怎么做。
面向对象是将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。
面向对象更加强调运用人类在日常的思维逻辑中采用的思想方法与原则,如抽象、分类、继承、聚合、多态等。
这样说还是太过于抽象了,我们来举个例子:
问题:把大象装进冰箱要怎么做
面向过程:
1.把冰箱门打开
2.抬起大象
3.将大象放进冰箱
4.把冰箱门关闭
这四个步骤是四个相互独立的函数,强调的是动作。
面向对象:
1.创造一个可以开关冰箱门的冰箱
2.创造一个可以进出冰箱的大象
3.让大象自己进冰箱
在这里,我们只做了一个动作,让大象自己进冰箱,而且我们还可以让大象自己出来。
面向对象注重的是让某个对象来完成,而不是如何来完成。
在编程的世界,我们就是上帝,为什么我们要疲劳于安排一个细致入微的事情?让他自己来吧。
面向对象三原则
- 封装
- 继承
- 多态
类和对象
类和对象是面向对象的核心概念。
- 类是对一类事物的描述,是抽象的、概念上的定义。
- 对象是实际存在的该类事物的每个个体,因而被称为实例
- 一切皆为对象
可以理解为类是一个抽象概念的人,对象是确确实实存在的一个人。
面向对象程序设计的重点是类的设计。
类的设计,其实就是类的成员的设计。
Java类及类的成员
现实世界的生物体,大到鲸鱼,小到蚂蚁,都是由最基本的细胞构成的,同理,Java代码世界是由多个不同功能的类构成的。
而细胞则是由细胞核、细胞质等等组成的,java中用到的类class也是由很多成员组成,常见的有:
- 属性:对应类中的成员变量
- 方法:对应类中的成员方法
示例:
class OOPTest {
public static void main(String[] args) {
}
}
class Person{
// 属性
String name;
int age;
boolean isMale = true;
// 方法
public void eat(){
System.out.println("人可以吃饭");
}
// 方法也可以使用参数
public void sleep(int date){
System.out.println("人可以睡觉,睡了"+date+"小时");
}
// 参数可以是各种数据类型
public void talk(String language){
System.out.println("人可以说话,使用的是:"+language);
}
}
类的实例化
使用类前需要进行类的实例化,我们在做键盘录入时使用了一个类Scanner,在使用它内部的方法时,需要先进行实例化:
Scanner scan = new Scanner(System.in);
那么在使用我们自己创建的类的时候,也需要这样:
class OOPTest {
public static void main(String[] args) {
Person zhangSan = new Person();
zhangSan.name = "法外狂徒";
zhangSan.age = 19;
zhangSan.isMale = true;
System.out.println("姓名是:"+zhangSan.name);
System.out.println("年龄是:"+zhangSan.age);
zhangSan.eat();
// 在使用有参数的方法时,需要传参,参数需要和接收参数的类型一致
zhangSan.sleep(8);
zhangSan.talk("Chinese");
}
}
class Person{
// 属性
String name;
int age;
boolean isMale;
// 方法
public void eat(){
System.out.println("人可以吃饭");
}
public void sleep(int date){
System.out.println("人可以睡觉,睡了"+date+"小时");
}
public void talk(String language){
System.out.println("人可以说话,使用的是:"+language);
}
}
然后又两个需要注意的地方:
一是对象空间相互独立,二是对象空间可共享
class OOPTest {
public static void main(String[] args) {
Person zhangSan = new Person();
// 使用"."操作符,可以获取类中的属性和方法
// 重写类中的属性
zhangSan.name = "法外狂徒";
zhangSan.age = 19;
zhangSan.isMale = true;
System.out.println("姓名是:"+zhangSan.name);
System.out.println("年龄是:"+zhangSan.age);
// 调用类中的方法
zhangSan.eat();
// 在使用有参数的方法时,需要传参,参数需要和接收参数的类型一致
zhangSan.sleep(8);
zhangSan.talk("Chinese");
// --------------------
Person liSi = new Person();
System.out.println(liSi.name);
// 输出null,每new一次,就在堆空间中生成了一个对象空间,liSi指向的这个对象空间是空白的。
// 这两个对象都有自己一套独立的属性与方法,互不干涉。
// --------------------
Person wangWu = zhangSan;
wangWu.name = "王五";
// 法外狂徒改了名字,希望重新生活
System.out.println("姓名是:"+zhangSan.name);
// 但是因为类是引用数据类型,我们再建立wangWu对象时引用了zhangSan,所以wangWu的对象空间就是zhangSan的对象空间
}
}
class Person{
// 属性
String name;
int age;
boolean isMale;
// 方法
public void eat(){
System.out.println("人可以吃饭");
}
public void sleep(int date){
System.out.println("人可以睡觉,睡了"+date+"小时");
}
public void talk(String language){
System.out.println("人可以说话,使用的是:"+language);
}
}
从内存角度解释这两点
他们之间也是用指针指向的。
属性与局部变量
局部变量是指在方法内的变量。
class Person{
// 属性
String name;
int age;
boolean isMale;
// 方法
public void eat(){
String food = "大饼"; //局部变量
System.out.println("人可以吃饭");
}
public void sleep(int date){
System.out.println("人可以睡觉,睡了"+date+"小时");
}
public void talk(String language){
System.out.println("人可以说话,使用的是:"+language);
}
}
-
相同点
- 都是变量
- 先声明,再使用
- 变量都有其作用域
-
不同点
-
在类中声明的位置的不同,属性直接定义在类的一对{}内,局部变量定义在方法内
-
属性可以在声明时使用权限修饰符,大部分局部变量都不能使用
-
属性有默认初始化值,局部变量没有(在使用局部变量时,一定要显式赋值)
-
方法
语法:
权限修饰符 [方法修饰符] 返回值类型 方法名(形参列表){
方法体;
}
权限修饰符:
- private
- public
- 缺省(xing)
- protected
方法修饰符:
- static
- final
- abstract
返回值类型:
- 有返回值
如果方法有返回值,就必须在方法声明时指定返回值的类型,且必须使用return返回对应类型的数据。
而且,返回必须形成闭环:
if (age > 18){
return name;
}else{
return "TOM";
} else内必须要有return来形成闭环。
- 没有返回值
如果方法没有返回值,使用void即可。
方法名:
- 就是一个标识符,遵循标识符声明规则
- 见名知意
形参列表:
- 可以不声明,也可以声明多个。
- 格式: 形参类型1 形参名1,形参类型2 形参名2,...
return语句:
- 必须在方法中使用
- 终止一个方法
- 针对于有返回值的方法,使用return可以将它返回
- 在return后,不可以写语句
调用方法:
- 使用"."操作符
- 方法中定义了形参列表必须传参
- 在方法中又调用了本身方法:递归方法
例子:
class Person{
// 属性
String name;
int age;
boolean isMale;
// 方法
public void eat(){
System.out.println("客户吃饭");
}
public void sleep(int hour){
System.out.println("休息了"+hour+"个小时");
}
public String getName(){
if (age > 18){
return name;
}else{
return "TOM";
}
}
public String getNation(String nation){
String info = "我的国籍是" + nation;
return info;
}
}
练习:
class OOPTest {
public static void main(String[] args) {
// 练习1
Student s1 = new Student();
s1.name = "小明";
s1.age = 20;
s1.major = "大数据";
s1.interests = "看书";
s1.say();
Teacher t1 = new Teacher();
t1.name = "张老师";
t1.age = 40;
t1.teachAge = 15;
t1.course = "大数据";
t1.say();
// 练习2
Circle c1 = new Circle();
double s = c1.S(3);
System.out.println(s);
// 练习3
// 要20个学生对象,总不能真的建立20个对象。
// 这就需要数组了。
Students[] stus = new Students[20];
for(int i=0 ; i<stus.length ; i++){
stus[i] = new Students();
stus[i].number = i + 1;
stus[i].state = (int)(Math.random() * (6) + 1);
stus[i].score = (int)(Math.random() * (100) + 1);
}
// 你要使用的方法在 OOPTest这个类里面,所以要实例化对象
OOPTest test = new OOPTest();
// 问题1
test.searchState(stus,3);
// 问题2
test.sort(stus);
test.print(stus);
}
// 代码改进
// 我们刚才解决这两个问题,用到的几个功能:遍历数组,排序,查找数组。
// 如果我们把它也功能化了呢?这样的话可以直接调用函数来使用他们。
/**
* 遍历Student数组
* @param stus 要遍历的数组
*/
public void print(Students[] stus){
for(int i=0 ; i<stus.length ; i++){
stus[i].say();
}
}
/**
* 查找Students数组中指定年级的信息
* @author 作者
* @param stus 要查找的数组
* @param state 要找的年级
*/
public void searchState(Students[] stus,int state){
for(int i=0 ; i<stus.length ; i++){
if(stus[i].state == state){
stus[i].say();
}
}
}
/**
* 对Students数组按照成绩升序排序
* @author 作者
* @param stus 要排序的数组
*/
public void sort(Students[] stus){
for(int i=0 ; i<stus.length - 1 ; i++){
for(int j=0 ; j<stus.length - 1 ; j++){
if(stus[j].score > stus[j + 1].score){
Students temp = stus[j];
stus[j] = stus[j + 1];
stus[j + 1] = temp;
}
}
}
}
}
/**
* 练习1
* 建立一个学生类,一个老师类,详情如下:
* 学生类(Student):
* name:string:姓名
* age:int:年龄
* major:string:专业
* interests:string:兴趣
* say():void:输出学生信息
*
* 教师类(Teacher):
* name:string:姓名
* age:int:年龄
* teachAge:int:工龄
* course:string:教授科目
* say():void:输出教师信息
*/
class Student{
String name;
int age;
String major;
String interests;
public void say(){
System.out.println("姓名:" + name);
System.out.println("年龄:" + age);
System.out.println("专业:" + major);
System.out.println("兴趣:" + name);
}
}
class Teacher{
String name;
int age;
int teachAge;
String course;
public void say(){
System.out.println("姓名:" + name);
System.out.println("年龄:" + age);
System.out.println("工龄:" + teachAge);
System.out.println("科目:" + course);
}
}
/**
* 练习2
* 设计一个类Circle计算圆的面积
*/
class Circle{
double pi = 3.14;
public double S(int bj){
double s = bj*bj*pi;
return s;
}
}
/**
* 练习3
* 定义一个类Studets,包含三个属性:
* number:int:学号
* state:int:年级(1-6)
* score:int:成绩(1-100)
*
* 创建20个学生对象,学号为1到20,年级和成绩都有随机数指定。
*
* 问题1:打印3年级的学生信息
* 问题2:按照学生成绩升序排序
*/
class Students{
int number;
int state;
int score;
public void say(){
System.out.println("学号:"+number+" 年级:"+state+" 成绩:"+score);
}
}
一切皆为对象
-
做一个对比,Linux中有种说法叫做"一切皆为文件",是因为他将服务,网络,硬件设备等等都封装进了一个文件中,通过对这个文件内容或者文件位置的修改就可以变更这个服务。这就是Linux的一切皆为文件,因为各种各样文件封装了各类服务。
-
同样在面向对象中的"一切皆为对象",是因为我们将功能,结构封装到类中,然后通过类的实例化创建对象,通过对象来实现我们想要实现的功能。
-
涉及到面向对象语言与其他方面交互时的表现形式,例如文件对象,网络对象等等。
匿名对象
public class InstanceTest {
public static void main(String[] args) {
Phone p = new Phone();
p.sendEmail();
p.playGame();
// 正常情况下new一个对象是要有一个变量来接收的。
// 但是有时候只需要使用一个类一下,没有必要声明一个变量来占栈内存。
// 这样就可以使用匿名对象。
new Phone().sendEmail();
// 匿名对象都是相互独立的,毕竟每new一次,就开辟一块空间。
new Phone().price = 1999.00;
new Phone().showPrice(); // 输出0.0
// 匿名对象经常使用的一种方式
Phone mail = new Phone();
mail.show(new Phone());
}
}
class Phone{
double price;
public void sendEmail(){
System.out.println("发送邮件");
}
public void playGame(){
System.out.println("玩游戏");
}
public void showPrice(){
System.out.println("价格为:"+price);
}
public void show(Phone phone){
phone.sendEmail();
phone.playGame();
}
}
方法重载
在同一个类中,允许存在一个以上的同名方法,只要他们的参数个数或者参数类型不同即可。
总结就是"两同一不同":同一个类,方法名相同,参数列表不同
public class OverloadTest {
public static void main(String[] args) {
}
public void getSum(int i , String s){
System.out.println("1");
}
public void getSum(double i , double s){
System.out.println("2");
}
public void getSum(String s , int i){
System.out.println("3");
}
public void getSum(int i , int s){
System.out.println("4");
}
// 以下这几种都不构成方法重载
// public int getSum(int i , String s){ 改返回值类型
// return 0;
// }
// public void getSum(int j , String c){ 改形参名称
// System.out.println("7");
// }
// private void getSum(int i , String s){ 改权限修饰符
// System.out.println("8");
// }
}
我们只需要在调用时输入提前规定好的参数列表,就可以调用指定的方法。
但是数字呢?假设有浮点数和整数的方法,该怎么调用?
public class OverloadTest {
public static void main(String[] args) {
OverloadTest over = new OverloadTest();
over.getSum(1, 1);
// 他会输出4,但是把第四个重载注销掉后,会输出2.
// 数字类型会按照参数顺序优先寻找int类型,添加第五个重载,注销第四个重载,会输出5.
// 然后添加第6个,注销4,5,会输出6.
// 只有真的找不到int类型,才会找其他类型。
}
public void getSum(int i , String s){
System.out.println("1");
}
public void getSum(double i , double s){
System.out.println("2");
}
public void getSum(String s , int i){
System.out.println("3");
}
public void getSum(int i , int s){
System.out.println("4");
}
public void getSum(int i , double s){
System.out.println("5");
}
public void getSum(double i , int s){
System.out.println("6");
}
// 以下这几种都不构成方法重载
// public int getSum(int i , String s){ 改返回值类型
// return 0;
// }
// public void getSum(int j , String c){ 改形参名称
// System.out.println("7");
// }
// private void getSum(int i , String s){ 改权限修饰符
// System.out.println("8");
// }
}
练习:
public class OverloadTest1 {
public static void main(String[] args) {
OverloadTest1 o = new OverloadTest1();
// 练习1
o.mOL(2);
o.mOL(2,4);
o.mOL("123");
// 练习2
o.max(2, 4);
o.max(2.0, 4.0);
o.max(2.0, 4.0, 6.0);
}
/**
* 练习1:
* 定义三个重载方法并调用,方法名为mOL
* 三个方法分别接收一个int参数、两个int参数、一个字符串参数
* 分别执行平方运算并输出结果、相乘并输出结果、输出字符串信息
* 在主类main中可以分别用参数区别调用三个方法。
*/
public void mOL(int a){
System.out.println(a * a);
}
public void mOL(int a,int b){
System.out.println(a * b);
}
public void mOL(String a){
System.out.println(a);
}
/**
* 练习2:
* 定义三个重载方法并调用,方法名为max
* 第一个方法求两个int值的最大值
* 第二个方法求两个double值的最大值
* 第三个方法求三个double值的最大值
*/
public void max(int a,int b){
int max = a > b ? a : b;
System.out.println(max);
}
public void max(double a,double b){
double max = a > b ? a : b;
System.out.println(max);
}
public void max(double a,double b,double c){
double max = a > b ? (a > c ? a : c) : (b > c ? b : c);
System.out.println(max);
}
}
不定长参数
Java提供了Varargs机制,允许直接定义能和多个实参相匹配的形参。
简单来说就是方法中定义一个不定长参数,然后传入n个实参,这些实参必须保证和不定长参数类型一致,然后不定长参数会将他们转换为一个数组。
public class MethodArgsTest {
public static void main(String[] args) {
MethodArgsTest m = new MethodArgsTest();
m.show(110,"1","2","3");
}
// 在形参列表里使用 参数类型 ... 形参名来声明不定长参数,也可以搭配其他参数,但是不定长参数必须在最后。
public void show(int a , String ... stra){
System.out.println(a);
for(int i=0 ; i<stra.length ; i++ ){
System.out.println(stra[i]);
}
}
// 不定长参数和数组不算重载
// public void show(int a , String[] stra){
// System.out.println(a);
// for(int i=0 ; i<stra.length ; i++ ){
// System.out.println(stra[i]);
// }
// }
}
值传递
形参:方法声明时的参数
实参:方法调用时实际传给形参的参数值
-
Java的实参值如何传入方法呢?
Java里方法的参数传递方式只有一种:值传递。即将实参的副本传入方法,实参不受影响。
形参是基本数据类型,将实参基本数据类型变量的数据值传递给形参
形参是引用数据类型,将实参引用数据类型变量的地址值传递给形参
用人话说就是,传入基本数据类型,形参发生的改变不会影响实参;传入引用数据类型,形参发生的改变会影响实参。
public class ValueTransgerTest {
public static void main(String[] args) {
ValueTransgerTest v = new ValueTransgerTest();
int m = 10;
v.one(m);
System.out.println("方法外:" + m); // 10
int[] a = new int[]{1,2,3};
v.two(a);
System.out.print("方法外:");
System.out.println(a[0]); // 20
}
public void one(int m){
m = 20;
System.out.println("方法内:" + m); // 20
}
public void two(int[] m){
m[0] = 20;
System.out.print("方法内:");
System.out.println(m[0]); // 20
}
}
练习:
import java.io.PrintStream;
public class ValueTransgerTest1 {
public static void main(String[] args) {
// 练习1:编写method方法,仅输出a=100,b=200
int a = 10;
int b = 20;
method(a,b);
System.out.println("a="+a);
System.out.println("b="+b);
// 练习2:定义一个int类型数组 int[] arr = new int[]{12,3,3,34,56,77,432};
// 让数组的每个位置上的值取除以首位置的元素,得到的结果作为该位置的新值。
int[] arr = new int[]{12,3,3,34,56,77,432};
for(int i = arr.length-1 ; i >= 0 ; i--){
arr[i] = arr[i] / arr[0];
}
}
public static void method(int a , int b){
// 思路:在方法内打印 a=100,b=200 然后结束程序运行。
a *= 10;
b *= 10;
System.out.println("a="+a);
System.out.println("b="+b);
System.exit(0); // 结束程序运行
}
public static void method1(int a , int b){
// 思路:重写System.out.println方法,如果输入的是a=10则输出a=100;如果输入的是b=20则输出b=200.
PrintStream ps = new PrintStream(System.out){
@Override
public void println(String x){
if("a=10".equals(x)){
x = "a=100";
}else if("b=20".equals(x)){
x = "b=200";
}
super.println(x);
}
};
System.setOut(ps);
}
}
递归
一个方法在他的方法体内调用了他自身。
方法的递归包含了一种隐式的循环,他会重复执行某段代码,但是这种重复执行无须循环控制。
递归一定要向已知方向递归,否则这种递归就会变成无穷递归,类似于死循环。
public class RecursionTest{
public static void main(String[] args) {
RecursionTest r = new RecursionTest();
int a = r.sum(100);
System.out.println(a);
}
// 计算1-100之间所有自然数的和
public int sum(int num){
if(num == 1){
return 1;
}else{
return num+sum(num - 1);
}
}
}
关于递归的底层原理我这篇博文最下面说到过:https://blog.csdn.net/qq_41106844/article/details/105554024
封装
封装的作用是什么:
我们现在要用洗衣机,是只需要按一下开关,设置一下洗涤模式呢,还是必须要知道这个洗衣机的工作原理和内部架构呀!
为什么需要封装:
程序设计追求“高内聚,低耦合”,高内聚是类的内部数据操作细节自己完成,不允许外部干涉,低耦合是仅对外暴露少量的方法用于使用。
封装的含义是什么:
隐藏对象内部的复杂性,只对外公开简单的接口,便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来,这就是封装的设计思想。
public class AnimalTest {
public static void main(String[] args) {
Animal ani = new Animal();
ani.setName("小黄");
ani.setAge(10);
ani.setLegs(4);
ani.show();
String name = ani.getName();
int age = ani.getAge();
int legs = ani.getLegs();
System.out.println(name+" "+age+" "+legs);
}
}
class Animal{
// 既然要进行封装,首先要将属性封在类中,使用private权限修饰符声明属性权限为私有
private String name;
private int age;
private int legs;
// 然后我们开放几个方法,用户调用这些方法就可以对类中的属性赋值,但是我们可以规范用户的输入
// 总不能洗衣机上只有8个功能,我们选第10个功能。
public void setName(String n){
if(n != ""){
name = n;
}else{
name = "名字丢失了";
}
}
public void setAge(int a){
if(a >= 0){
age = a;
}else{
age = 0;
}
}
public void setLegs(int l){
if(l >=0 && l % 2 == 0){
legs = l;
}else{
legs = 0;
}
}
// 但是如果我们想要调用一下类里面的属性来看一下他们的值该怎么办呀!
// 我们不能说为了一个功能而造成正常功能的缺失,所以我们定义了几个方法专门用于外部的查询。
public String getName(){
return name;
}
public int getAge(){
return age;
}
public int getLegs(){
return legs;
}
// 最后再来个一键启动的方法。
public void show(){
System.out.println("他是"+name+",今年"+age+"岁了,长了"+legs+"条腿");
}
}
使用setXXX和getXXX的目的:
隐藏一个类不需要对外提供的实现细节
使用者只能使用提前定义好的方法来访问数据,可以方便的加入控制逻辑,限制对属性的不合理修改
便于修改,增强代码的可维护性。
权限修饰符
修饰符 | 同一个类 | 同一个包 | 不同包的子类 | 同一个工程 |
---|---|---|---|---|
private | yes | |||
缺省 | yes | yes | ||
protected | yes | yes | yes | |
public | yes | yes | yes | yes |
public类可以在任意地方被访问。
缺省类只能被同一个包里的类访问。
4种权限可以用来修饰类及类的内部结构:属性、方法、构造器、内部类。
修饰类只能使用缺省或public
构造器
任何一个类,都有构造器。
我们再创建类对象的时候,会怎么做呢?
Person p = new Person();
首先是类名,决定我们实例化一个什么类型;接着是变量名,表示承载这个类的是哪个变量;接着是关键字new,那么最后这个Person(),是什么?
你也许会说是类名呀,但是他为什么带括号呢?java的类在定义时并没有括号。
难道是一个方法吗?但是我们并没有定义这个方法,而且想想之前的scanner类:
Scanner s = new Scanner(System.in);
他里面甚至都可以传参数。
这就是构造器,一个和类同名,但是多个括号的类成员。如果我们没有显式定义构造器,就默认是空参构造器。
public class PersonTest {
public static void main(String[] args) {
Person p = new Person();
p.eat();
}
}
class Person{
String name;
int age;
public void eat(){
System.out.println("吃饭");
}
public void study(){
System.out.println("学习");
}
// 构造器 语法:权限修饰符 类名()
public Person(){
System.out.println("创建person对象");
}
}
当你按住Ctrl键点击new后的Person时,会自动指向下面我们定义的构造器这里。而且我们运行后,构造器内定义的语句也会执行。
我们也可以给他定义个参数。
public class PersonTest {
public static void main(String[] args) {
Person p = new Person("小明");
p.eat();
}
}
class Person{
String name;
int age;
public void eat(){
System.out.println(name+"在吃饭");
}
public void study(){
System.out.println("学习");
}
// 构造器
public Person(String n){
name = n;
}
}
既然构造器是类的成员,那么他可不可以重载呢?
public class PersonTest {
public static void main(String[] args) {
Person p = new Person("小明");
p.eat();
}
}
class Person{
String name;
int age;
public void eat(){
System.out.println(name+"在吃饭");
}
public void study(){
System.out.println("学习");
}
// 构造器
public Person(){
System.out.println("创建person对象");
}
public Person(String n){
name = n;
}
}
构造器的特征:
与类同名
它不声明返回值类型
不能被static final synchronized abstract native 修饰,不能有return返回值
构造器的作用:
创建对象,给对象进行初始化
比如我们规定人出生必须洗澡,那么将洗澡放到构造器中,在构造人时就会自动洗澡了,没有必要再一个一个的告诉他们。
练习:
public class PersonTest {
public static void main(String[] args) {
Person p = new Person();
System.out.println(p.age);
// ----------
TriAngle t = new TriAngle(3.0,5.0);
double base = t.getBase();
double height = t.getHeight();
System.out.println(base*height);
}
}
// 让类实例化对象的年龄默认为18.
class Person{
int age;
public Person(){
age = 18;
}
}
/**
* 编写TriAngle类,
* 类中声明私有的底边长base和高height
* 同时声明公共方法访问私有变量,
* 此外提供必要的构造器,使用这些公共方法,计算三角形面积
*/
class TriAngle{
private double base;
private double height;
public void setBase(double b){
base = b;
}
public double getBase(){
return base;
}
public void setHeight(double h){
height = h;
}
public double getHeight(){
return height;
}
public TriAngle(){
}
public TriAngle(double b,double h){
base = b;
height = h;
}
}
属性赋值的过程
- 先找点操作符赋值,例如:p.age = 18;
- 再找构造器中赋的值
- 接着是显示初始化赋的值
- 最后是默认初始化赋的值
关键字:this的使用
方法中有时会出现与属性同名的变量,那么怎么加以区分呢?就可以使用this关键字。
public class ThisTest {
}
class Person{
private String name;
private int age;
public void setName(String name){
// 标识码命名必须见名知意,这就造成了属性与局部变量重名,
// 这时可以使用this关键字,this关键字标识的变量为属性。
this.name = name;
}
public void setAge(int age){
this.age = age;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
}
this关键字的作用:
this关键字可以用来修饰属性,方法,构造器
this关键字可以理解为当前对象,即 this.name == Person.name
如果方法的形参或局部变量与属性重名且同时出现在同一方法中,this不可省
练习:
public class ThisTest {
public static void main(String[] args) {
Boy b = new Boy("罗密欧",21);
b.shout();
Girl g = new Girl("朱丽叶",20);
g.marry(b);
Girl g1 = new Girl("祝英台",21);
int a = g.compare(g1);
if (a == 1){
System.out.println(g.getName()+"大");
}else if(a == -1){
System.out.println(g1.getName()+"大");
}else{
System.out.println("一样大");
}
}
}
/**
* 编写两个java类:
* Boy类:
* setName(String name)
* setAge(int age)
* getName()
* setAge()
* marry(Girl girl) :打印男孩想娶的女孩
* shout(int age) :打印男孩是否可以结婚
*
* Girl类:
* setName(String name)
* setAge(int age)
* marry(Boy boy) :打印女孩想嫁的男孩
* compare(Girl girl):比女孩年龄大小
*/
class Boy{
private String name;
private int age;
public void setName(String name){
this.name = name;
}
public void setAge(int age){
this.age = age;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
public Boy(String name,int age){
this.name = name;
this.age = age;
}
public void marry(Girl girl){
System.out.println("我想娶"+girl.getName());
}
public void shout(){
if(this.age >= 22){
System.out.println("你可以去领证了");
}else{
System.out.println("你可以再谈几年恋爱");
}
}
}
class Girl{
private String name;
private int age;
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public Girl(String name,int age){
this.name = name;
this.age = age;
}
public void marry(Boy boy){
System.out.println("我想嫁给"+boy.getName());
boy.marry(this);
// this表示当前对象
}
public int compare(Girl girl){
if(this.age > girl.age){
return 1;
}else if(this.age < girl.age){
return -1;
}else{
return 0;
}
}
}