一、Java类及类的成员
1、现实世界的生物体,大到鲸鱼,小到蚂蚁,都是由最基本的细胞构成的。同理, Java代码世界是由诸多个不同功能的类构成的。
2、现实生物世界中的细胞又是由什么构成的呢?细胞核、细胞质、.那么,Java中用类class来描述事物也是如此。常见的类的成员有:
属性:对应类中的成员变量 // 属性(JAVA)=成员变量(C++)=field(高频率 国外API文档叫法)=域、字段(国外翻译)
行为:对应类中的成员方法 // 方法(JAVA) = 函数(C++)=method (国外原生API文档叫法)
特别说明:JAVA是真正面向对象的语言,(不像C++,保留了面向过程的部分)因此万物皆对象。所以不存在单独的函数,所有函数都是类的方法。
二、类的创建:
语法:
class 类名 {
【static class;】 //类本身也是对象,class为每个类的隐藏静态属性,代表类本身所代表的对象 类名.class
//属性
【权限修饰符】 数据类型 成员变量名【=值】; //权限修饰符就是外部调用成员变量的权限大小(封装性的时候专门讲)
/*添加类属性,可以先给类属性赋值,实例化对象的时候如果没给对象属性赋值,这个值可以就先用作对象的默认值*/
......
//方法
【权限修饰符】 返回类型 方法名(形参列表){ //给类添加方法,一般方法的权限都是public,偶尔有private
函数体;
}
}
2.1 类的属性 成员变量(非static)
1、类的属性就是类成员变量,成员变量有默认值(和数组的默认值一样)
即:(1)数组元素为整型(byte short int long): 默认初始化为 0
(2)数组元素为浮点型(float double):默认初始化为0.0
(3)数组元素为字符型(char):默认初始化为0 (AscII 的0 对应的符号)
(4)数组元素为boolean型:默认初始化为false (boolean值false的底层是0)
(5)数组元素为引用数据类型(String,类):默认初始化为null
2、成员变量在堆内存中,可添加权限修饰符(public,private,protected)限制外部调用成员变量的范围。(具体参考封装性)
2.2类的方法 (static,final,abstract来修饰的方法后面说)
方法用于描述类具有的功能。比如: Math类:sqrt()\random() 方法
方法声明:
【权限修饰符】 返回值类型 方法名(【形参列表】){
函数体; } // 如果方法带返回值,函数体中一定要有 return 表达式;
说明:
(1)权限修饰符就是外部调用函数时的权限大小(默认权限为缺省,后文有权限修饰符介绍)
(2)如果函数有返回值,返回值类型可以是基本数据类型也可以是引用数据类型,此时,函数体内必须有"return 表达式"(且表达式的值必须是声明的返回值类型)。如果函数没有返回值,方法声明时,用void替换返回值类型,此时函数体内return后面啥都不能加,表示直接结束函数(函数体中的if场景可能会用到)。或者函数体内不写return。
(3)方法名作为标识符,要符合函数标的命名规范(首字母小写,后面每个单词首字母大写xxYyyZzz(小驼峰式)),并要做到“见名知意”。
(4)形参列表:可以声明0个,1个,或多个形参
格式: 数据类型1 形参1,数据类型2 形参2,数据类型3 形参3,......
(5)return关键字的作用:
- 结束函数 (函数中嵌套了循环,return终止函数的同时,也退出了循环)
- 返回指定类型的数据(void没返回值)
(6)方法的定义中,可以调用当前类的属性或方法。但不可用方法中再定义方法(嵌套定义方法)
- 函数进阶:
(7)函数重载(overload)
同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可(即参数列表不同),编译器会根据实参类型和实参个数自动选择对的类方法。比如:Array类中 sort方法重载了很多次,可以给byte排序,可以给int排序,可以给char排序
说明:跟方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系。
(8)可变参数形参
JDK5中新加的特性,用来替代通过数组传形参。
说明:没有可变参数形参之前,想要给方法传入多个参数,只能靠数组。比如public void show(String[ ] strs){ }
可变个数形参格式: 数据类型...变量名
说明:
I.当调用可变个数形参的方法时,传入的参数个数可以是:0个,1个,2个...N个。但是数据类型必须一致。
II.可变参数同样可以和类中的同名方法构成重载。特别注意,由于历史遗留,此方法形参会和public void show(String[ ] strs){ }中的形参同名,如果同时出现这两种形参且方法名又刚好相同,编译器会报错(不构成重载)。
III.可变参数形参必须声明在形参的末尾,并且只能有一个,要不编译器会没法识别从而报错。比如: 方法名(数据类型1 形参1,数据类型2 形参2,...数据类型N 可变参数形参N)
IV.可变参数形参由于是替代数组,也是一个对象,也拥有自己的属性和方法。其中一个属性就是length,看到底传过来了多少个实参。
exp:
class vargTest { //定义一个拥有可变形参方法的类vargTest
public void show(String...strs){ //定义类中的可变形参方法show。
System.out.println("show me"};
}
public static void main(String[ ] args){
vargTest test = new 构造器();
test.show("hello"); //一个实参可以调用show方法
test.show("hello","showgirl"); //两个实参也可以调用show方法
test.show(); //0个实参也可以调用show方法
}
(9)函数递归(recursion) 写递归真的费脑子啊
函数递归指一个方法体内调用它自身。
说明:函数递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制
注意:递归一定要向已知方向递归,视情终止递归,否则这种递归会变成无穷递归,类似于死循环。
exp:计算1-100自然数的和
思路:求n个数的和,就是求(n-1)个数的和+n,求(n-1)个数的和,就是求(n-2)个数的和+(n-1).....一直到1
public int sum(int n){
if(n==1) { //当n一直减到1的时候,必须停止递归,要不死循环了。然后1+2作为返回值,去解上一层sum,直到最外层
return 1; }
else return n + sum(n-1);
}
总结:递归解题步骤,写出递推公式,找出终止条件,翻译成递归代码。
三、类的封装
1、概述
程序设计追求“高内聚”,“低耦合”
高内聚:类的内部数据操作细节自己完成,不允许外部干涉。
低耦合:仅对外暴露少量的方法用于使用。
隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。
2、封装对象内部属性的方法:权限修饰符private
语法: private 数据类型 属性名【=属性值】;
说明:此时该属性被隐藏。只能通过public方法,对该属性进行赋值。同理,也只能通过类的public方法,调用这个属性。总之,一旦封装了类的属性,就要写公共的get和set方法来获取或者设置属性值。
- 附录:权限修饰符
(1)JAVA规定了4种权限:private、缺省、protected、public
(2)4种权限可以用来修饰类及类的内部结构:属性、方法、构造器、内部类
四、类的构造器
构造器 constructor(C++的构造函数)
构造器的作用:创建对象
说明:如果没有显式定义类的构造器,系统会默认隐式提供一个空参的构造器。但是注意,一旦显式定义了类构造器,系统将不再提供默认的类空参构造器
语法: (一般类的第一个方法 就是构造器)
权限修饰符 类名(【形参列表】){} //权限默认为 类的权限
说明:构造器也可以"重载",通过构造器“重载”,可以初始化对象属性(也可添加一些行为)
五、扩展1 this指针
this指针 指向当前类的本身,可以看做当前类。写在类的内部 //后面类实例化成啥具体对象,这个this指针就指啥对象
说明:由于this是当前类本身,自然有如下语法:
this(形参列表) ; //调用本类的其他构造器(不能自己调自己),主要用于少写代码
规定:如果用this调用其他构造器,必须放在当前代码块的首行。(由于只有一个首行,隐喻不能调2个构造器)
this.属性名; //调用类的属性,通常可以省略this,遇到和形参重名了,就没法省略了
this.方法名; //调用类的方法
应用场景1:调用当前类本身的构造器
很多时候,一个类会写多个构造器,每个构造器之间又有80%相同代码+20%自己的特有代码。80%相同代码如果行数很多,就会显得冗余。 此时可以通过 this(形参列表),调用构造器。
应用场景2:调用类属性
由于要尽量符合标识符命名规范中的“见名知意”,类的属性名,和设置类属性值的形参名都是一个东西,取一样的名字最好识别。虽然由于形参和类属性名作用域不同,不会报错,但是编译器会搞混。因此,可以给类属性名加上this,变成this.属性名,表示此变量时属性,而非形参
exp: class Person{
private int age;
public Person(){
函数体; //假装有100行代码
}
public Person(int age){ //构造器的形参名age 和 属性名age 一样了
this(); //直接调用构造器,就少写了(复制)100行代码
this.age = age //用了this指针,编译器就能很好的区分了
}
}
扩展2:JavaBean 一种符合特定规范的Java类
JavaBean是一种Java语言写成的可重用组件。
符合如下JavaBean标准的Java类,都是JavaBean
JavaBean标准:
1、类是公共的 //其他类可见
2、有一个无参的公共构造器 //方便在其他类方法中创建这个类的对象,但此时属性都是默认值
3、有属性,且有对应的get、set方法 //方便在其他类方法中,给这个对象赋值
扩展3 UML设计图
六、对象 (类的实例化)
1、创建类的对象
语法:
类名 变量名 = new 类构造器(【形参列表】);
说明:
(1)Java允许实例化一个空对象,后面给对象属性赋值。(c要求构造函数赋值)
(2)给对象的属性赋值之前,对象可先用类的属性值作为默认值,如果类没赋默认值,成员变量本身还有默认值(规则和数组默认值一样)
2、调用对象的属性、方法
(1)调用对象属性
语法: 变量名.属性名=值; //注意属性的权限,如果是private,类外只能通过public函数赋值
(2)调用对象方法
语法:变量名.方法名(实参列表);
说明:相当于在方法的函数体作用域内,把实参赋值给形参。
注意:Java不像C++可以直接按引用传递基本数据类型 即通过&变量名就把数据穿了。
因此 Java要实现变量数据交换等功能,需要先把要交换的数据封装在一个对象里。然后把对象作为实参传给方法(方法必须接收引用数据类型)。
exp:
3、同一类对象之间的赋值 (区别C++ 为啥JAVA没有运算符重载)
对象赋值和数组一样,类对象之间赋值是给的对象的指针,此时两个对象同时指向堆的同一内存块。并不是将某一对象赋值给另一对象。 由于赋值的是指针,自然就没法像c++一样重载运算符“=”,操作想赋值的属性给对象。
结论:引用类型的赋值,都是赋值地址。
exp:
Student Tao = new Student();
Tao.age = 29;
Student Zou = new Student();
Zou=Tao; // 将Tao对象的指针 赋给 Zou对象,此时对象Tao 和 Zou 都指向同一内存块
Zou.age =23; //此时修改Zou,就是修改Tao
System.in.println(Tao.age); //自然Tao.age 也被改为23
4、对象内存解析 (暂时不讨论方法,不讨论static,不讨论private)
exp:
class Person{
String name;
int age = 1; //设置类 age属性的默认属性值为1
boolean isMale ;
}
注意:Tom是String,又是引用,在方法区,不在堆里面。这里只是方便
5、匿名对象
如果创建的对象,没有显示的赋值给一个变量名。即为匿名对象。
特征:匿名对象只能调用一次。
exp: class Phone{ //定义一个Phone类
double price=2000;
public void sendEmail(){
}
public void playGame(){
}
}
new Phone().price=3999; //创建了一个Phone匿名对象,并将其price属性设置为3999
System.in.println("new Phone().price");//又创建了一个Phone匿名对象,并打印其价格,为默认值2000
注意:两次创建的匿名对象不是一个对象,是两个对象。所以price属性,是完全不一样的(匿名对象只能调用一次。)。
- 匿名对象应用方法 (思路:让匿名对象不再匿名,即可多次使用)
如果将匿名对象作为实参传递给另一个新函数。就相当于把新创建的匿名对象的地址值赋值给了新函数的形参。此时匿名对象不再匿名,他的名字就是新函数形参名,就可以多次使用。