zoukankan      html  css  js  c++  java
  • 4.2 方法详解

    一、方法的所属性

    方法由传统的函数发展而来,方法与传统的函数显著不同:在结构化编程中,函数是一等公民,这个程序由一个个函数组成;在面向对象编程语言里,类才是一等公民,整个系统由一个个类组成。因此在Java语言里,方法不能独立存在,方法必须属于类或对象。
    方法的所属性:
    (1)方法类似于函数。但与函数不同的是,方法不能存在,方法必须定义在类里面。
    (2)方法一定要有执行者,必须通过类或对象来调用方法。从逻辑上来看,该方法属于类本身,应该用类来调用
        如果该方法有static修饰,该方法属于类本身,应该用类调用;
        如果该方法无static修饰,该方法属于对象本身,应该用对象调用
    【规则】如果调用同一个类中方法,可以省略调用者,此时系统会默认添加调用者。如果方法是无static方法,添加this作为默认调用者;如果方法是static方法,添加类作为调用者。

    class Method_attribute 
    {
        //定义一个普通方法
        public void nonStaticMethod()
        {
            System.out.println("这是一个普通方法");
        }
        //定义一个static方法
        public static void StaticMethod()
        {
            System.out.println("这是一个类方法");
        }
        //在同一个类中一个方法调用另外一个方法
        public void test()
        {
            this.nonStaticMethod();
            this.StaticMethod();
            StaticMethod();//省略的是主调类
        }
        public static void main(String[] args) 
        {
            var p=new Method_attribute();
            //此时test()方法中的两个this代表对象p
            p.test();        
        }
    }
    ---------- 运行Java捕获输出窗 ----------
    这是一个普通方法
    这是一个类方法
    这是一个类方法
    输出完成 (耗时 0 秒) - 正常终止
    

    二、方法参数的传递机制

    Java里方法是不能独立存在的,调用方法时必须使用类或对象作为主调者。如果声明方法时,包含了形参声明,则调用方法时必须给这些形参指定参数值,调用参数时传给形参参数值也称为实参。
    Java里面参数传递方式只有一种:值传递。所谓值传递,就是将实际参数的副本(复制品)传入方法内,而参数本身不会受到影响。

    class ParamTransferTest 
    {
        public static void swap(int a,int b)
        {
            //实现变量a和b的值交换
            //定义一个临时变量来保存a的值
            var temp=a;
            a=b;
            b=temp;
            System.out.println("swap方法里,a的值为:"+a+",b的值为:"+b);
        }
        public static void main(String[] args) 
        {
            int a=6;
            int b=9;
            swap(a,b);
            System.out.println("交换结束后,a的值为:"+a+",b的值为:"+b);
        }
    }
    ---------- 运行Java捕获输出窗 ----------
    swap方法里,a的值为:9,b的值为:6
    交换结束后,a的值为:6,b的值为:9
    
    输出完成 (耗时 0 秒) - 正常终止
    

    Java程序从main()方法开始执行,main()方法开始定义了a、b两个局部变量,如图所示:

    程序从main()函数开始执行,当程序进入swap()方法时,系统分配了两个栈区,将mian()方法中的变量a、b的副本传入swap()方法,而不是a、b本身。
    程序在swap()方法中,进行变量a、b交换的值,交换结束后,内存中的存储情况:

    两个示意图可以发现,mian()方法栈区中a、b值并未发生改变,程序只改变swap()栈区中的变量a、b。这就是值传递的实质:当系统开始执行方法时,系统为形参初始化,就是把实参变量的值赋给方法的形参变量,方法里操作的并不是实参变量。
    再看一个例子:

    class DataWrap
    {
        int a;
        int b;
    }
    
    public class ReferenceTransferTest 
    {
        public static void swap(DataWrap dw)
        {
            //下面实现的dw的两个成员的变量值的交换
            var temp=dw.a;
            dw.a=dw.b;
            dw.b=temp;
            System.out.println("swap()方法里,a成员变量的值是:" + dw.a + ",b成员变量的值是:"+dw.b);
        }
    
        public static void main(String[] args) 
        {
            var dw=new DataWrap();
            dw.a=6;
            dw.b=9;
            swap(dw);
            System.out.println("swap()方法里,a成员变量的值是:"+dw.a+",b成员变量的值是:"+dw.b);
        }
    }
    ---------- 运行Java捕获输出窗 ----------
    swap()方法里,a成员变量的值是:9,b成员变量的值是:6
    swap()方法里,a成员变量的值是:9,b成员变量的值是:6
    
    输出完成 (耗时 0 秒) - 正常终止
    

    上面结果swap()方法里和mian()方法里的两个变量a、b都发生了改变。这很容易产生错觉:调用swap()方法时,传入的时dw本身,而不是它的复制品。
    下面分析一下程序执行的过程:
    (1)程序从mian()方法开始执行:
    var dw=new DataWrap();
    dw.a=6;
    dw.b=9;
    这里创建了一个DataWrap对象,并赋给引用变量dw。堆内存保存该对象本身,栈内存保存的该对象的引用变量。

    (2)执行swap(dw);
    接下来main()方法开始调用swap()方法,mian(0方法并未结束,系统会为main()和swap()开辟两个栈区,用于存放mian()和swap()方法里的局部变量。调用swap(0方法时,dw作为实参传入swap()方法,同样采用值传递:把main()方法里的dw变量赋值给swap()方法里的dw形参,从而完成swap()方法的dw形参初始化。下图显示main()方法中实参dw传入swap()方法后的存储示意图:

    系统只复制了dw变量,但未复制DataWrap对象。
      不管程序在swap()方法中还是在mian()方法的操作dw变量时,实际操作的都是堆内存中的DataWrap对象,他们引用的是同一个变量。因此在swap()方法中交换了dw所引用的a、b两个成员变量后,可看到在main()方法中dw引用变量引用的a、b变量的值也发生了改变。

    实际上,我们在创建swap()方法时,将

    public static void swap(DataWrap dw);中的dw换个符号表示理解起来就更容易,不易混淆

    三、形参个数可变的方法

    Java允许定义形参个数可变的参数,从而允许为方法指定数量不确定的形参。如果在定义方法时,在最后一个形参的类型后增加三点(...),则表示该形参可以接受多个参数值,多个参数被当作数组传入。

    class  VarArgs
    {
        public void test(int a,String... names)
        {
            System.out.println("a参数:"+a);
            System.out.println("names数组的长度:"+names.length);
            for(int i=0;i<names.length;i++)
            {
                System.out.println(names[i]);
            }
        }
        public static void main(String... args) 
        {
            VarArgs va=new VarArgs();
            va.test(2,"efdf","fdfs","fjgd");
            va.test(34,new String[]{"孙悟空","猪八戒"});
        }
    }
    class  VarArgs
    {
        public void test(int a,String... names)
        {
            System.out.println("a参数:"+a);
            System.out.println("names数组的长度:"+names.length);
            for(int i=0;i<names.length;i++)
            {
                System.out.println(names[i]);
            }
        }
        public static void main(String... args) 
        {
            VarArgs va=new VarArgs();
            va.test(2,"efdf","fdfs","fjgd");
            va.test(34,new String[]{"孙悟空","猪八戒"});
        }
    }
    ---------- 运行Java捕获输出窗 ----------
    a参数:2
    names数组的长度:3
    efdf
    fdfs
    fjgd
    a参数:34
    names数组的长度:2
    孙悟空
    猪八戒
    
    输出完成 (耗时 0 秒) - 正常终止
    

    va.test(2,"efdf","fdfs","fjgd");   va.test(34,new String[]{"孙悟空","猪八戒"});
    这两种多形参传入方法都可以,但是第一种方法更加简洁。

    类型[] 形参名

    类型...写法的好处是:
    调用方法时更加方便,即可直接传入多个元素,系统会自动将它们封装为数组
    也可直接传入数组
    确定:类型... 这种写法只能作为形参列表的最后一个形参
    【暗示】一个方法只能由一个形参个数可变的形参

    再看一个例子:

    class VarIntTest 
    {
        public int add(int... num)
        {
            int result=0;
            for (int i=0;i<num.length;i++)
            {
                result+=num[i];
            }
            return result;
        }
        public static void main(String[] args) 
        {
            var p=new VarIntTest();
            System.out.println(p.add(1,2,3,4));
        }
    }
    ---------- 运行Java捕获输出窗 ----------
    10
    
    输出完成 (耗时 0 秒) - 正常终止
    

    四、递归方法

    一个方法体可以调用它自身,被称为方法递归。方法递归包含一种隐式循环,它会重复执行某段代码,但这种重复执行无需循环控制。
    例子:已知f(0)=,f(1)=4,f(n+2)=2f(n+1)+f(n),其中n是大于0的整数,求f(10)。
    递归必须向一直方向进行,如果使用f(n)=f(n+2)-2
    f(n+1),f(10)=f(12)-2f(11),朝着大段方向进行,大段是未知的。故递归有一条重要的规则:必须向着已知方向递归。
    对上面的迭代式子进行变形,f(n)=2
    f(n-1)+f(n-2)

    class Recursive 
    {
    	public static int fn(int n)
    	{
    		if(n==1)
    			return 1;
    		else if(n==2)
    			return 4;
    		else
    		{
    			return fn(n-2)+2*fn(n-1);
    		}
    	}
    	public static void main(String[] args) 
    	{
    		
    		System.out.println(new Recursive().fn(5));
    	}
    }
    ---------- 运行Java捕获输出窗 ----------
    53
    
    输出完成 (耗时 0 秒) - 正常终止
    

    五、方法重载

    Java允许在同一个类里定义多个同名的方法,只要形参列表不同就行。如果同一个类中包含两个或以上的方法名相同,但形参列表不同,则称为方法重载。
    因此在Java中确定一个方法需要三个因素:
    1、调用者,也就是方法的所属者,既可以是类,也可以是对象。
    2、方法名,方法的标识。
    3、形参列表,当调用方法时,系统会根据传入实参列表匹配。

    方法重载要求两同一不同:同一个类的方法名相同,形参列表不同
    修饰符不同(如static修饰的方法和无static方法修饰的方法)方法,带返回值或不带返回值或返回值类型不同的方法,形参列表不同的方法都不能算重载。

    class OverLoad 
    {
    	public void test()
    	{
    		System.out.println("这是一个无参数的test方法");
    	}
    	public void test(String... strs)
    	{
    		System.out.println("这是一个形参个数可变的test方法");
    	}
    
    	protected void test(int a)
    	{
    		System.out.println("这是一个带int参数的test方法");
    	}
    	/*
    	public static void test()
    	{
    		System.out.println("这是一个static修饰的test方法");
    	}
    	//OverLoad.java:17: 错误: 已在类 OverLoad中定义了方法 test()
    	*/
    	/*
    	public int test()
    	{
    		System.out.println("带返回值的test方法");
    	}
    	//OverLoad.java:23: 错误: 已在类 OverLoad中定义了方法 test()
    	*/
    
    	public static void main(String[] args) 
    	{
    		OverLoad ov=new OverLoad();
    		ov.test();
    		ov.test("hello","world");
    		ov.test(5);
    	}
    }
    ---------- 运行Java捕获输出窗 ----------
    这是一个无参数的test方法
    这是一个形参个数可变的test方法
    这是一个带int参数的test方法
    
    输出完成 (耗时 0 秒) - 正常终止
    
  • 相关阅读:
    vue双向数据绑定原理
    vue-router原理
    CSS选择器优化
    静态资源的渲染阻塞
    什么是Base64?Base64的原理是什么?
    OAuth的解释
    AMD与CMD的区别
    AMD的实现
    组件通信的几种方式
    关于istream_iterator<int>(cin)和istream_iterator<int>()的一点分析
  • 原文地址:https://www.cnblogs.com/weststar/p/12345967.html
Copyright © 2011-2022 走看看