目录
第三个例子:新建对象
属性
声明
属性是类的对象的相同特征,语法和变量的声明很像,即
- 属性类型 属性名称 = 属性值
初始化
如果在声明属性的同时进行了初始化赋值,那么所有对象的属性就完全相同。如果希望每个对象的属性不一致,那么不用在声明属性的时候进行初始化,属性会在构造对象的时候默认初始化,默认初始化的值取决于属性的类型:
- byte, shout, int, long =>0;
- float, double =>0.0;
- boolen flg = false;
- char = 空字符;
- 引用数据类型 => null;
作用域
变量的作用域非常小,只在当前的大括号内有效;属性不仅仅在当前类中有效,而且可以随着对象在其他地方使用。变量使用前必须初始化,否则会出现错误;属性可以不用初始化,因为JVM会会帮助我们自动完成初始化。
方法
声明
方法是对象的行为和功能,声明语法是
- [方法的返回值类型][void] 方法名() { 逻辑代码 };//void 表示方法的返回结果:没有结果
调用方式
方法的调用方式是
- 对象.方法名();
方法和属性的区分:从多个对象中提炼出共通的特征和行为,特征作为类的属性声明,行为作为类的方法声明。简单提炼方法:根据语言描述中的词性来简单区分,名词 or 动词。
如,使用用户账号和密码进行登录;
- 用户(独立的名词,可抽取为类),
- 账号、密码(名词,是用户的两个特征,从属于用户,作为属性),
- 登录(动词,用户登录是行为,从属于用户,作为方法).
package chapter04;
public class Object05_Method {
public static void main(String[] args) {
//面向对象——方法
//练习:使用用户账号和密码进行登录
User user = new User();
boolean registerResult = user.register();
if(registerResult) {
System.out.println("注册成功");
boolean loginResult = user.login();
if(loginResult) {
System.out.println("登录成功");
}else {
System.out.println("登录失败");
}
}else {
System.out.println("注册失败");
}
}
}
class User {
String account;
String password;
boolean register() {
System.out.println("用户注册");
return true;//假定值
}
boolean login() {
System.out.println("用户登录");
return false;//假定值
}
}
方法参数
使用外部数据控制方法内部实现的操作,使用的是参数语法实现。从外部获取数据控制方法的内容逻辑实现的语法操作,我们称之为参数,也叫方法参数。方法的参数语法类似于变量(类型 名称),但是需要在方法后面的小括号中使用。参数可以有多个,多个参数用逗号隔开,小括号中的所有参数称为参数列表。如果方法声明了参数,那么调用时必须传递参数,否则会发生错误。形式如下:
- 返回值类型 [void] 方法名( 参数类型 参数名称);
- 返回值类型 [void] 方法名( 参数类型1 参数名称1,参数类型2 参数名称2 );
当方法名有多个参数时,需要注意:参数个数、类型、顺序要匹配;当参数类型(含义)相同,但是个数不确定时,可以采用特殊的参数语法声明,即可变参数。如果参数中还包含其他含义的参数,那么可变参数应该声明在最后。语法:
- 参数类型 . . . 参数名称
package chapter04;
public class Object05_Method_Param {
public static void main(String[] args) {
//方法参数
Hello h = new Hello();
String name = "zhangsan";
int age = 30;
//传递参数
h.sayHello(name,age);
//可变参数
h.test();
h.test("zhangsan");
h.test("zhangsan" + "lisi" + "wangwu");
h.test1(30);
h.test1(30,"zhangsan");
h.test1(30,"zhangsan" + "lisi" + "wangwu");
}
}
class Hello {
void sayHello(String name,int age) {
//使用参数
System.out.println("Hello " + name + "," + age);
}
void test(String...name) {
System.out.println(name);
}
void test1(int age,String...name) {
System.out.println(name);
}
}
但是结果跟预期不一样,除去第一行,打印出来的都是地址值,为什么呢?
因为此处 name 可变参数是数组,直接输出是地址值,因此需要循环取值打印
采用for循环,格式:
- for (参数类型 参数名 :可变参数名){ 打印(参数名) }
void test(String...name) {
for(String str: name) {
System.out.println(str);
}
}
void test1(int age,String...name) {
for(String str: name) {
System.out.print(age + ",");
System.out.println(str);
}
}
方法参数——传值方式
看例子预期输出结果,与运行后的结果相比较。
第一个例子:基本数据类型
package chapter04;
public class Object05_Method_Param1 {
public static void main(String[] args) {
//方法参数——传值方式
int i = 10;
test(i);//test方法中的i是main方法中i的复制品,二者在不同的内存位置
//改变test中的i后,test方法弹栈,main方法中的i没有改变
System.out.println(i);
}
public static void test(int i) {
i = i + 10;
}
}
可以看到输出结果是10,而不是20,为什么呢?这里要了解Java虚拟机的执行过程:
Java虚拟机在执行任意一个方法时都会在栈内存中开辟出一小块内存空间,用于存储方法内部的参数、变量、执行逻辑等,称为栈帧。而CPU指向固定的位置,并执行这个位置的栈帧。因此在创建新方法时会对栈帧进行压栈操作,在执行完一个方法后会对栈帧进行弹栈操作。
对例子中的两个方法画图分析:
在test方法执行完毕后,对应test栈帧被弹出,CPU继续执行main方法。两个方法中的 i 存储在不同的内存区域,后续main方法中打印的仍然是main中 i 的值,因此仍为10。
第二个例子:字符串拼接
package chapter04;
public class Object05_Method_Param1 {
public static void main(String[] args) {
//方法参数——传值方式
String s = "abc";
test(s);
System.out.println(s);
}
public static void test(String s) {
s = s + 10;
}
}
可以看到输出结果是abc,而不是abc10。接下来解释为什么:
字符串 "abc" 是引用数据类型,放在堆中,s 存储字符串的地址。test方法中 s 传递的是引用变量的地址,开始时同样指向"abc",但在拼接字符串后会指向产生的新地址,新地址中是"abc10"。test 方法执行完毕后弹栈,接着执行main方法,打印的是main中 s 的指向的字符串,因此仍为abc。
第三个例子:新建对象
package chapter04;
public class Object05_Method_Param1 {
public static void main(String[] args) {
//方法参数——传值方式
User1 user1 = new User1();
user1.name = "zhangsan";
test(user1);
System.out.println(user1.name);
}
public static void test(User1 user1) {
user1.name = "lisi";
}
}
class User1{
String name;
}
可以看到这次打印的结果改变了,使用同样的分析方式:
首先main方法创建了一个对象,放在堆中,属性为name,然后给name赋值为"zhangsan"。接下来执行test方法,其中的user指向同一个位置,把属性name的值改为"lisi"。接下来继续执行main方法,于是打印name值为"lisi"。
总结:Java中方法参数的传递为值传递。如果是基本数据类型,传递的是数值;如果是引用数据类型,传递的是引用地址。