目录
一、基本介绍
在某些情况下,我们需要定义成员方法。比如人类:除了有属性以外,人类还有一些行为:说话,跑步;这时候就需要用成员方法才能完成,对Person类完善
二、快速入门
1)
public class Object01{
public static void main(String[] args){
//方法使用 先创建对象 然后调用方法即可
Person p1 = new Person();
p1.speak();//调用方法 程序执行到这里会去person类里找speak方法
}
}
class Person{
String name;
int age;
//public 表示是公开的 void表示方法没有返回值
//speak() 表示方法名 ()表示形参列表
//{}方法体,可以写我们要执行的代码
//System.out...表示方法内容,输出一句话
//方法写好后,不去调用不会输出语句
public void speak(){
System.out.println("我是一个好人");
}
}
2)
public class Method01{
public static void main(String[] args){
p1.cal01();
}
}
class Person{
public void cal01(){//计算从1+...+1000
int sum = 0;
for(int i = 1; i <= 1000 ;i++){
sum += i;
}
System.out.println(sum);
}
}
3)
p1.cal02(5);//调用方法的时候 给形式参数一个数值 即把5传给了n
//()表示目前有一个形参n 形式参数,可以接受用户输入
public void cal02(int n){
int sum = 0;
for(int i = 1; i <= n ;i++){
sum += i;
}
System.out.println(sum);
}
4)
public class Method01{
public static void main(String[] args){
Person p1 = new Person();
int returnRes = p1.getSum(10,20);//方法getSum返回的值赋给returnRes
System.out.println(returnRes);
System.out.println(p1.getSum(10,20));//同上,这个方法可以直接打印出来
}
}
class Person{
//public 表示方法是公开的
//int 表示方法执行后 返回一个int可以自行接收
//getSum表示方法名
//(int num1,int num2)表示可以接受用户传入的两个参数
//return res表示把res的值返回
public int getSum(int num1,int num2){
int res = num1 + num2;
return res;
}
}
三、方法调用的机制
Person p1 = new Person();
int returnRes = p1.getSum(10,20);//方法getSum返回的值赋给returnRes
System.out.println(returnRes);
public int getSum(int num1,int num2){
int res = num1 + num2;
return res;
}
第一行创建对象,会在堆创建一个的数据空间,这行代码在main栈(只是为了方便区分,底层不会对栈进行命名)执行,意思就是p1指向堆里的一个数据空间,叫数据对象;
第二行先执行等号右边,会在栈内创建一个数据空间,getSum栈(底层没有这个名字只是为了方便区分),会跳到对应的方法,public int getSum,随后执行方法内容,执行完return res之后从getSum栈有一个int数据类型返回给main栈内的p1.getSum(),注意,当返回完之后getSum栈会销毁。然后执行等号的左边,赋值给returnRes。
总结
1.当程序执行到方法时,会开辟一个独立的空间(栈空间)
2.当方法执行完毕,或者执行到return语句时,就会返回,返回完之后数据空间会销毁
3,返回到调用方法的地方
4.返回后,继续执行后面的方法
5.当main方法执行完毕,整个程序会退出
四、成员方法的好处
//传统方法
public class Method02{
public static void main(String[] args){
int[][] map = {{0,0,1},{1,1,1},{1,1,3}};
for(int i = 0;i < map.length;i++){
for(int j = 0;j < map[i].length;j++){
System.out.print(map[i][j]);
}
System.out.print(" ");
}
}
}
传统的解决方式就是直接遍历,而且如果要多次调用会很麻烦,只能复制一份放在对应的地方,代码冗余。考虑定义一个类MyTools 然后写一个方法
public class Method02{
public static void main(String[] args){
int[][] map = {{0,0,1},{1,1,1},{1,1,3}};
Mytools tools = new Mytools();
tools.for01(map);
}
}
class Mytools{
public void for01(int[][] map){//map的名字和main栈的不冲突,可以使用
for(int i = 0;i < map.length;i++){
for(int j = 0;j < map[i].length;j++){
System.out.print(map[i][j]);
}
System.out.print(" ");
}
}
}
好处,可以实现细节的封装,其他用户来调用即可
五、成员方法的定义
public 返回数据类型 方法名(参数列表..){//方法体
语句;
return 返回值;
}
1.参数列表:表示成员方法输入 cal(int n)
2.数据类型(返回类型):表示成员方法输出,void表示没有返回值
3.方法主题:表示为了实现某一功能代码块
4.return语句不是必须的
5.访问修饰符(作用是控制 方法使用的范围)
如果不写会有默认值 (有四种 public protected 默认 private)
返回数据类型
1.一个方法最多只有一个返回值 需要返回多个数据可以使用数组
2.返回数据可以是任意类型
3.如果方法要求有返回值,那么在方法内容里一定要包含return返回语句,返回可以是一个变量或者常量,需要满足自动转换
4.如果方法体是void 可以不写return 或者只写return 后面不能带有返回的值
方法名
应该使用驼峰命名法 setSum showArr等 aaBbbCcc类型,方法名应当有一定含义
形参列表
1.一个方法可以有0个参数,也可以有多个参数,中间用逗号隔开,例如speak(int n1,int n2)
2.参数类型可以为任意类型,包含基本类型和引用类型比如(int arr[])
3.调用带参数的方法时,一定对应着参数列表传入相同类型或者兼容类型
4.方法调用时,实际传入的参数叫实参,实参和形参传的兼容类型、个数、顺序,必须一致
方法体
里面写完成功能的具体语句,可以为输入、输出、变量、循环等等,但不能再定义方法,即方法不能嵌套方法
方法调用
1.同一个类的方法调用:直接调用即可比如print(参数)
class A{
public void print(int n1){
System.out.print("ok" + n1);
}
public void sayOk(){
print(10)
}
}
2.跨类中的方法A类调用B类:需要通过对象名调用。比如 对象名.方法名(参数);
class A{
public void m1(){
B hello = new B();
hello.h1();
}
}
class B{
public void h1(){
System.out.print("b类的h1方法");
}
}
3.特别说明:跨类的方法调用和方法的访问修饰符相关,到访问修饰符时会详讲
跟pubilc、包有很大的关系
六、成员方法的练习
1.编写方法判断奇偶
import java.util.Scanner;
public class MethodExercise01{
public static void main(String[] args){
int enter = 0;
Scanner myScanner = new Scanner(System.in);
System.out.println("请输入一个数,判断是否是偶数");
enter = myScanner.nextInt();
AA num = new AA();
num.determineParity(enter);
}
}
class AA{
public void determineParity(int n1){
if(n1 % 2 == 0){
System.out.println("这个数是偶数");
}
else{
System.out.println("这个数是奇数");
}
}
}
2.打印对应列行数的字符
import java.util.Scanner;
public class MethodExecrise02{
public static void main(String[] args){
int hang = 0;
int lie = 0;
char zifu;
Scanner myScanner = new Scanner(System.in);
System.out.println("请输入行数");
hang = myScanner.nextInt();
System.out.println("请输入列数");
lie = myScanner.nextInt();
System.out.println("请输入字符");
zifu = myScanner.next().charAt(0);
PrintOut outPut = new PrintOut();
outPut.arrPrint(hang,lie,zifu);
}
}
class PrintOut{
public void arrPrint(int hang,int lie,char zifu){
for(int i = 0;i < hang;i++){
for(int j = 0;j < lie;j++){
System.out.print(zifu + "\t");
}
System.out.println();
}
}
}
七、成员传参的机制
1.基本数据类型
public class MethodParameter{
public static void main(String[] args){
int a = 10;
int b = 20;
AA aa = new AA();
aa.swap(a,b);
System.out.println(" a=" + a + "\tb=" + b);
}
}
class AA{
public void swap (int a,int b){
System.out.println("交换前的值a=" + a + "\tb=" + b);
int temp = a;
a = b;
b = temp;
System.out.println("交换后的值a=" + a + "\tb=" + b);
}
}
这里是拷贝赋值,所以改变方法内的元素不会改变main方法内的数值的值
2.引用类型
public class MethodParameter02{
public static void main(String[] args){
B myTools = new B();
int[] arr = {10,20,50,15,100};
myTools.printArr(arr);
myTools.swap(arr);//注意传递数组应该是数组名而不加[]
myTools.printArr(arr);
}
}
// class Persong{
// String name;
// int age;
// }
class B{
public void swap (int arr[]){
for(int i = 0;i < arr.length; i++){
int min = 0;
for (int j = 0; j < i; j++){
if(arr[j] > arr[j + 1]){
min = arr[j];
arr [j] = arr[j + 1];
arr [j + 1] = min;
}
}
}
printArr(arr);
}
public void printArr(int arr[]){
System.out.println("");
for(int i = 0; i < arr.length; i++){//打印数组
System.out.print(arr[i] + "\t");
}
}
}
很好理解,因为是引用赋值,所以数值的改变会影响指向的堆内的值,所以访问的同一地址的值都会发生改变
意思就是在引用赋值中,形参也会影响实参
public class MethodParameter03{
public static void main(String[] args){
Person p = new Person();//声明对象,实例化一个对象
B b = new B();
p.name = "jack";//对象属性赋值
p.age = 20;
b.test200(p);
System.out.println("main方法内的p.age = " + p.age);
}
}
class Person {
String name;
int age = 0;
}
class B{
public void test200(Person p){//这里面的Person p是声明了一个对象
//在实际参数向形式参数的传递中可以不开辟空间 前提是要开过这个空间
p.age = 20000;
}
}
很好理解,main方法内的值也发生了改变,因为是同一地址指向
注意,对于方法间的数据传递机制还有
public class MethodParameter03{
public static void main(String[] args){
Person p = new Person();//声明对象,实例化一个对象
B b = new B();
p.name = "jack";//对象属性赋值
p.age = 20;
b.test200(p);
System.out.println("main方法内的p.age = " + p.age);
}
}
class Person {
String name;
int age = 0;
}
class B{
public void test200(Person p){//这里面的Person p是声明了一个对象
//在实际参数向形式参数的传递中可以不开辟空间 前提是要开过这个空间
p = null;
}
}
现在p置空,main方法内的p.age并不会抛出一个异常,因为在test200方法内改变的是形式参数,我的理解 实参在向形参传递的过程中test200内的 p = p;前一个p是test200的形式参数,后一个是main传来的实际参数,
随后 p = null 这时候改变的是test200的形式参数,所以p并没有办法影响main的属性
如果是
public class MethodParameter03{
public static void main(String[] args){
Person p = new Person();//声明对象,实例化一个对象
B b = new B();
p.name = "jack";//对象属性赋值
p.age = 20;
b.test200(p);
System.out.println("main方法内的p.age = " + p.age);
}
}
class Person {
String name;
int age = 0;
}
class B{
public void test200(Person p){//这里面的Person p是声明了一个对象
//在实际参数向形式参数的传递中可以不开辟空间 前提是要开过这个空间
// p.age = 20000;
// p = null;
p = new Person();
p.name = "tom";
p.age = 999;
}
}
注意,这时候在test200内创建了一个新的空间,意思就是形式参数的p不接收实参的地址值了,它新建了个空间指向堆里的地址,随后改变值什么的都不会影响main函数里面的值
八、练习 克隆对象
public class MethodExercise03{
public static void main(String[] args){
Person p = new Person();
MyTools tools = new MyTools();
p.name = "jack";
p.age = 100;
Person p2 = tools.copyPerson(p);
//声明对象Person p2,不需要创建新空间,这里的p2和Mytools的p2不一样
p.name = "tom";//验证是否是独立对象
System.out.println(p.name + p.age);
System.out.println(p2.name + p2.age);
}
}
class Person {
String name;
int age = 0;
}
class MyTools{//1.返回类型应该是Person 2.方法的形参应该是Person p
//3.方法体,创建一个新对象,并复制属性返回即可
public Person copyPerson(Person p){
Person p2 = new Person();
p2.name = p.name;//属性的复制
p2.age = p.age;//属性的复制
return p2;//返回p2
}
}
//也可以使用==判断地址是否相同
System.out.println(p == p);//true 是同一个地址所以一样
System.out.println(p == p2);//false 不是同一个地址所以不一样