打怪升级之小白的大数据之旅(十)<Java基础语法之面向对象的方法>

打怪升级之小白的大数据之旅(十)

Java基础语法之面向对象的方法

上次回顾:

上一章,对面向对象的基本概念,定义以及使用等知识点进行介绍,本章对面向对象中的方法进行分享,个人理解,方法就可以理解为面向过程~~

方法

方法的概念

  • 方法也叫函数,是一个独立功能的定义,是一个类中最基本的共功能单元
  • 把一个功能封装为方法的目的是,可以实现代码重用,从而减少代码量
  • 就像上一节末尾我炒土豆的例子来说,当我将整个过程都封装进函数中,下一次我再想炒土豆丝的时候,直接调用这个函数,我就可以吃土豆丝了~~

成员方法的分类

根据修饰不同的方法,主要分为两大类:

  • 实例方法:
    – 没有static修饰的方法,必须通过实例对象调用
    – 这句话的意思就是,我想要切土豆丝,我需要先拿出一个土豆来,如果没有static修饰这个方法,也没有拿出一个土豆,那我只能切空气
  • 类(静态方法)
    – 有static修饰的方法,也叫类方法
    – 可以使用实例名.方法名()来调用,也可以使用类名.方法名()调用
    – 推荐使用类名.方法名()调用

方法的声明

  • 方法声明的位置必须在类中,并且在类的成员位置
  • 方法中不能嵌套方法
  • 语法格式:
【修饰符】 返回值类型 方法名(【参数列表:参数类型1 参数名1,参数类型2 参数名, ......){
        方法体;
        【return 返回值;} 
  • 格式说明:

    • [权限修饰符]: 修饰符后面详细讲,例如:public,static等都是修饰符
    • 返回值类型: 表示方法运行的结果的数据类型,与”return 返回值“搭配使用
      • 无返回值:void
      • 有返回值:可以是任意基本数据类型和引用数据类型
    • 方法名:给方法起一个名字,要符合标识符的命名规则,尽量见名知意,能准确代表该方法功能的名字
    • 参数列表:方法内部需要用到其他方法中的数据,需要通过参数传递的形式将数据传递过来,可以是基本数据类型、引用数据类型、也可以没有参数,什么都不写
    • 方法体:特定功能的代码
    • return:结束方法,可以返回方法的运行结果
      • 可以返回不同类型的数据,对应匹配的返回值类型。
      • 如果方法无返回值,可以省去return,并且返回值类型为void
  • 示例代码1

    	public class Person {
        
        public void hello(){
            System.out.println("hello world");
        }
    }
    
  • 示例代码2

/*
定义一个圆的图形类:
属性(成员变量):半径,
功能(成员方法):计算圆的面积,并返回面积
在测试类的main中,创建圆的2个对象,为半径属性赋值,调用两个方法进行测试
提示:圆周率为Math.PI
*/
class Circle{
	double radius;
	double area() {	
		return Math.PI * radius * radius;
	}
}

Circle不同的对象,半径值不同,那么面积也不同,所以这里area()是非静态的

  • 示例代码3
/*定义一个计算工具类CountTools:
方法1:求两个整数的最大值*/
class CountTools{
	static int max(int a, int b) {
        return a > b ? a : b;
	}
}

CountTools只是一个工具类,求两个整数最大值的功能,和CountTools对象无关,所以这里max方法声明为静态的更好,当然也可以声明为非静态的,就是调用的时候需要创建CountTools对象而已

方法的调用

注: 方法必须先声明后使用,不调用不执行,调用一次,执行一次

  • 实例方法的调用
    对象名.实例方法(【实参列表】)
    示例代码

    public class TestCircle {
    	public static void main(String[] args) {
    		Circle c1 = new Circle();
    		c1.radius = 1.2;
    		System.out.println("c1的面积:" + c1.area());
    		//非静态方法只能通过"对象."进行访问
    //		System.out.println("c1的面积:" + Circle.area());
            
    		Circle c2 = new Circle();
    		c2.radius = 2.5;
    		System.out.println("c2的面积:" + c2.area());
    	}
    }
    class Circle{
    	double radius;
    	public double area() {
    		return Math.PI * radius * radius;
    	}
    }
    
  • 类方法(静态方法)的调用
    格式1
    类名.类方法(【实参列表】)
    格式二
    对象名.类方法(【实参列表】) (不推荐)

  • 示例代码:

    public class TestCount {
    	public static void main(String[] args) {
    		System.out.println(CountTools.max(4, 1));
    		
    		//静态方法也可以通过“对象.”访问,就是麻烦点
    		CountTools c = new CountTools();
    		System.out.println(c.max(2, 5));
    	}
    }
    class CountTools{
    	static int max(int a, int b) {
    		return a > b ? a : b;
    	}
    }
    
  • 形参与实参

    • 形参:在定义方法时方法名后面括号中声明的变量称为形式参数(简称形参)即形参出现在方法定义时。
    • 实参:调用方法时方法名后面括号中的使用的值/变量/表达式称为实际参数(简称实参)即实参出现在方法调用时。
    • 形参实际上就是方法声明上的参数列表,调用方法时传入的任意匹配的数据
    • 实参就是调用方法时实际传入的数据
    • 调用方法时,实参列表必须根据形参列表完全匹配 ,参数的个数,类型,顺序都必须完全一致
    • 返回值类型必须与实际的返回数据类型匹配
  • 方法调用的注意事项

    • 调用方法时,实参的个数、类型、顺序必须要与形参列表一一对应
    • 调用方法时,如果方法有返回值,可以用与返回值类型相同的变量接受或直接处理返回值结果,如果方法的返回值类型是void,不需要也不能接收和处理返回值结果。

本类内的成员变量和方法访问

  • 在一个类的内部,调用本类的方法或成员变量时,直接用,不需要加“对象名.“和"类名.”
  • 例外情况:静态方法中不能直接访问本类的非静态的成员变量和成员方法
    ** 静态不能调用非静态
    ** 非静态可以调用静态
  • 示例代码1
    class Circle{
    	double radius;
    	
    	//写一个方法,可以返回“圆对象”的详细信息
    	String getDetailInfo(){
    		return "半径:" + radius + ",面积:" + area() +",周长:" + perimeter();
    	}
    	
    	//写一个方法,可以返回“圆对象”的面积
    	double area(){
    		return Math.PI*radius*radius;
    	}
    	
    	//写一个方法,可以返回“圆对象”的周长
    	double perimeter(){
    		return 2*Math.PI*radius;
    	}
    
    }
    
  • 示例代码2
    class Test{
    		
    	static void test(){
    		System.out.println("");
    	}
    	void method(){
    		 test();
    	}
        
        public static void main(String[] args){
            method();//错误
            test();//正确
        }
    }
    

方法调用内存分析

栈结构特点:先进后出,后进先出。
又到了内存分析了,方法的内存过程如下:

  • 方法不调用不执行,调用一次执行一次,

  • 每次调用会在栈中有一个入栈动作,即给当前方法开辟一块独立的内存区域,用于存储当前方法的局部变量的值,

  • 当方法执行结束后,会释放该内存,称为出栈,

  • 如果方法有返回值,就会把结果返回调用处,

  • 如果没有返回值,就直接结束,回到调用处继续执行下一条指令

  • 示例代码一:

    public class TestCount {
    	public static void main(String[] args) {
            int a = 4;
            int b = 2;
    		int m = CountTools.max(a, b));
    	}
    }
    class CountTools{
    	static int max(int a, int b) {
    		return a > b ? a : b;
    	}
    }
    

    示例代码1

  • 示例代码二:

    public class TestCircle {
    	public static void main(String[] args) {
    		Circle c1 = new Circle();
    		c1.radius = 1.2;
    		int area1 = c1.area();
    		
    		Circle c2 = new Circle();
    		c2.radius = 2.5;
    		int area2 = c2.area();
    	}
    }
    class Circle{
    	double radius;
    	public double area() {
    		return Math.PI * radius * radius;
    	}
    }
    

    示例代码2

  • 方法的内存分析(实例方法)实例方法的内存分析

  • 方法的内存分析(静态方法)
    静态方法的内存分析

  • 示例代码三

    public class Test {
    	public static void main(String[] args) {
    		int[] arr = {2,4,1,5,3};
    		
    		ArrayUtil.sort(arr);
    		
    		for (int i = 0; i < arr.length; i++) {
    			System.out.println(arr[i]);
    		}
    	}
    }
    class ArrayUtil{
    	public static void sort(int[] arr){
    		for (int i = 1; i < arr.length; i++) {
    			for (int j = 0; j < arr.length - i; j++) {
    				if(arr[j] > arr[j+1]){
    					int temp = arr[j];
    					arr[j] = arr[j+1];
    					arr[j+1] = temp;
    				}
    			}
    		}
    	}
    }
    

示例代码1

方法参数的值传递机制

方法的参数传递机制:实参给形参赋值

  • 方法的形参是基本数据类型时,形参值的改变不会影响实参;

  • 方法的形参是引用数据类型时,形参地址值的改变不会影响实参,但是形参地址值里面的数据的改变会影响实参,例如,修改数组元素的值,或修改对象的属性值。

  • Java中,方法传参只有值传递

    • 方法参数是基本数据类型,形参的改变不会影响实参
    • 方法参数是引用数据类型,形参的改变会影响实参
  • 注意:String、Integer等特殊类型容易错

  • 方法参数传递的内存分析
    方法参数传递的内存分析

  • 示例代码一

    class Test{
        public static void swap(int a, int b){
            int temp = a;
            a = b;
            b = temp;
    	}
    
    	public static void main(String[] args){
            int x = 1;
            int y = 2;
            swap(x,y);//调用完之后,x与y的值不变
        }
    }
    
  • 示例代码二

    class Test{
        public static void change(MyData my){
            my.num *= 2;
        }
        
        public static void main(String[] args){
            MyData m = new MyData();
            m.num = 1;
            
            change(m);//调用完之后,m对象的num属性值就变为2
        }
    }
    
    class MyData{
        int num;
    }
    
  • 示例代码三

    public class Test {
    	public static void main(String[] args) {
    		int[] arr = {2,4,1,5,3};
    		
    		ArrayUtil.sort(arr);
    		
    		for (int i = 0; i < arr.length; i++) {
    			System.out.println(arr[i]);
    		}
    	}
    }
    class ArrayUtil{
    	public static void sort(int[] arr){
    		for (int i = 1; i < arr.length; i++) {
    			for (int j = 0; j < arr.length - i; j++) {
    				if(arr[j] > arr[j+1]){
    					int temp = arr[j];
    					arr[j] = arr[j+1];
    					arr[j+1] = temp;
    				}
    			}
    		}
    	}
    }
    
  • 陷阱一 实参形参陷阱

    /*
    陷阱1:在方法中,形参 = 新new对象,那么就和实参无关了
    */
    class Test{
        public static void change(MyData my){
            my = new MyData();//形参指向了新对象
            my.num *= 2;
        }
        
        public static void main(String[] args){
            MyData m = new MyData();
            m.num = 1;
            
            change(m);//调用完之后,m对象的num属性值仍然为1
        }
    }
    
    class MyData{
        int num;
    }
    
  • 陷阱二 字符串和包装类陷阱

    public class Test {
    	public static void main(String[] args) {
    		StringUtil util = new StringUtil();
    		String str = "尚硅谷";
    		util.change(str);
    		System.out.println(str);//
    	}
    }
    class StringUtil{
    	public void change(String str){
    		str += "你好";//String对象不可变,一旦修改就会产生新对象
    	}
    }
    

方法重载

  • 在同一个类中,只要它们的参数列表不同,就可以可以存在多个同名的方法

  • 方法重载与修饰符和返回值类型无关

  • 参数列表不同:指的是参数个数不同,数据类型不同,数据类型顺序不同

  • 示例代码一 比较两个数据是否相等

    /*
    比较两个数据是否相等。参数类型分别为两个byte类型,两个short类型,两个int类型,两个long类型,并在main方法中进行测试。
    */
    public class Method_Demo6 {
        public static void main(String[] args) {
            //定义不同数据类型的变量
            byte a = 10;
            byte b = 20;
            short c = 10;
            short d = 20;
            int e = 10;
            int f = 10;
            long g = 10;
            long h = 20;
            // 调用
            System.out.println(compare(a, b));
            System.out.println(compare(c, d));
            System.out.println(compare(e, f));
            System.out.println(compare(g, h));
        }
        // 两个byte类型的
        public static boolean compare(byte a, byte b) {
            System.out.println("byte");
            return a == b;
        }
    
        // 两个short类型的
        public static boolean compare(short a, short b) {
            System.out.println("short");
            return a == b;
        }
    
        // 两个int类型的
        public static boolean compare(int a, int b) {
            System.out.println("int");
            return a == b;
        }
    
        // 两个long类型的
        public static boolean compare(long a, long b) {
            System.out.println("long");
            return a == b;
        }
    }
    
  • 示例代码二 求各种最大值

    /*
    用重载实现:
    定义方法求两个整数的最大值
    定义方法求三个整数的最大值
    定义方法求两个小数的最大值
    */
    //求两个整数的最大值
    public int max(int a,int b){
        return a>b?a:b;
    }
    	
    //求三个整数的最大值
    public int max(int a, int b, int c){
        return max(max(a,b),c);
    }
    	
    //求两个小数的最大值
    public double max(double a, double b){
        return a>b?a:b;
    }
    
  • 示例代码三 判断两个方法是否是合理的重载方法

        //判断如下两个方法是否构成重载:是
        class StringUtil{
        	public static String concat(char seperator, String... args){
        		String str = "";
        		for (int i = 0; i < args.length; i++) {
        			if(i==0){
        				str += args[i];
        			}else{
        				str += seperator + args[i];
        			}
        		}
        		return str;
        	}
        	public static String concat(String[] args){
        		String str = "";
        		for (int i = 0; i < args.length; i++) {
        			str += args[i];
        		}
        		return str;
        	}
        }
        //判断如下两个方法是否构成重载:不是
        class Count{
        	public static int getSum(int... nums){
        		int sum = 0;
        		for (int i = 0; i < nums.length; i++) {
        			sum += nums[i];
        		}
        		return sum;
        	}
        	public static int getSum(int[] nums){
        		int sum = 0;
        		for (int i = 0; i < nums.length; i++) {
        			sum += nums[i];
        		}
        		return sum;
        	}
        }
    
        class Test06_Overload_Problem2{
        	public static void main(String[] args){
        		System.out.println(sum(1,2));//(int a, int b)
        		System.out.println(sum(1,2,3));//(int... args)和(int a, int... args)都兼容,就有问题
        	}
        
        	//不调用编译没问题,但是调用时就有问题
        	public static int sum(int a, int b){
        		return a+b;
        	}
        	public static int sum(int... args){
        		int sum = 0;
        		for(int i=0; i<args.length; i++){
        			sum += args[i];
        		}
        		return sum;
        	}
        	public static int sum(int a, int... args){
        		int sum = a;
        		for(int i=0; i<args.length; i++){
        			sum += args[i];
        		}
        		return sum;
        	}	
        }
    

可变参数

  • 我们定义一个方法时,此时某个形参的类型可以确定,但是形参的个数不确定,那么我们可以使用可变参数

  • 格式
    【修饰符】 返回值类型 方法名(【非可变参数部分的形参列表,】参数类型... 形参名){ }

  • 注意事项:
    1). 一个方法最多只能有一个可变参数
    2). 如果一个方法包含可变参数,那么可变参数必须是形参列表的最后一个

  • 示例代码一 求n个整数的和

    public class ChangeArgs {
    	public static void main(String[] args) {
    		int[] arr = { 1, 4, 62, 431, 2 };
    		int sum1 = getSum1(arr);
    		System.out.println(sum1);
    
    		int sum2 = getSum2(arr);
    		System.out.println(sum2);
    		int sum3 = getSum2(1, 4, 62, 431, 2);
    		System.out.println(sum3);
    	}
    
    	// 完成数组 所有元素的求和
    	// 原始写法
    	public static int getSum1(int[] arr) {
    		int sum = 0;
    		for (int i = 0; i < arr.length; i++) {
    			sum += arr[i];
    		}
    
    		return sum;
    	}
    
    	// 可变参数写法
    	public static int getSum2(int... arr) {
    		int sum = 0;
    		for (int i = 0; i < arr.length; i++) {
    			sum += arr[i];
    		}
    		return sum;
    	}
    }
    
  • 示例代码二 求1-那个整数中的最大值

    public class ChangeArgs_Exer1 {
    	public static void main(String[] args) {
    		System.out.println(max(1));
    		System.out.println(max(5,3,2,6));
    	}
    
    	public static int max(int num, int... others){
    		int max = num;
    		for (int i = 0; i < others.length; i++) {
    			if(max < others[i]){
    				max = num;
    			}
    		}
    		return max;
    	}
    }
    
  • 示例代码三 字符串拼接

    // 需求一:返回n个字符串拼接结果,如果没有传入字符串,那么返回空字符串
    public class ChangeArgs_Exer2 {
    	public static void main(String[] args) {
    		System.out.println(concat());
    		System.out.println(concat("hello","world"));
    	}
    	public static String concat(String... args){
    		String str = "";
    		for (int i = 0; i < args.length; i++) {
    			str += args[i];
    		}
    		return str;
    	}
    }
    
    // 需求二:n个字符串进行拼接,每一个字符串之间使用某字符进行分割,如果没有传入字符串,那么返回空字符串
    public class ChangeArgs_Exer4 {
    	public static void main(String[] args) {
    		System.out.println(concat('+'));
    		System.out.println(concat('+',"hello","world"));
    	}
    	public static String concat(char seperator, String... args){
    		String str = "";
    		for (int i = 0; i < args.length; i++) {
    			if(i==0){
    				str += args[i];
    			}else{
    				str += seperator + args[i];
    			}
    		}
    		return str;
    	}
    }
    

递归

  • 方法自己调用自己
    • 递归的分类
    • 直接递归: 自己直接调用自己
  • 间接递归; 方法A调用B,方法B调用方法A
    注意:
    1). 递归需要出口,否则会造成死递归,类似死循环,会造成内存溢出
    2). 即使有出口,也不宜太多,否则会造成内存溢出(栈内存的溢出)
    ,
  • 理解递归,首先看这张图
    递归
    相信大家都看过盗梦空间吧,可以把盗梦空间的梦境理解为递归,现实世界就是最开始的方法,当根据需求需要自己调用自己后,就是递归,也就是盗梦空间进入了第一层梦境.在盗梦空间中,反复提到过,他们要醒来需要一个属于自己的物件,并且需要有死亡、坠落感等.如果缺失了出去的条件,就再也出不去了,递归也是如此,如果没有一个出口,就会陷入死循环,造成内存溢出.
  • 通过示例代码来加深对例如方法实现递归的印象:
    • 示例代码一: 计算1~100之间所有的自然数之和

    • 利用循环实现

      public class RecursionMethod1{
      	public static void main(String[] args) {
      		int sum = sum(100);
      		System.out.println("1-100的和:" + sum);
      	}
      
      	public static int sum(int n){
      		int sum=0;
              for(int i=1;i<=n;i++){
                  sum+=i;
              }
              return sum;
      	}
      }
      
    • 利用递归实现

      public class RecursionMethod1{
      	public static void main(String[] args) {
      		int sum = sum(100);
      		System.out.println("1-100的和:" + sum);
      	}
      
      	public static int sum(int n){
      		if(n == 1){
      			return 1;
      		}else{
      			return n + sum(n-1);
      		}
      	}
      }
      

      内存图分析:
      递归的内存图

    • 示例代码二: 利用递归实现斐波那契数列(Fibonacci)的第n个值
      规律:一个数等于前两个数之和,

      /*
      f(0) =1,
      f(1) = 1,
      f(2) = f(0) + f(1) =2,
      f(3) = f(1) + f(2) = 3, 
      f(4) = f(2) + f(3) = 5
      ...
      f(n) = f(n-2) + f(n-1);*/
      public class RecursionMethod3{
      	public static void main(String[] args) {
      		Count c = new Count();
      		
      		System.out.println("f(10):" + c.f(10));
      		System.out.println("f方法被调用的总次数:" + c.total);
      	}
      }
      class Count{
      	int total = 0;
      	public int f(int n){
      		total++;
      		if(n <= 1){
      			return 1;
      		}else{
      			return f(n-2) + f(n-1);
      		}
      	}
      }
      
    • 斐波那契数列递归思路:
      在这里插入图片描述

  • 阶乘的内存图分析
    在这里插入图片描述

对象数组

  • 对象数组,就是在以类为数据类型的数组,
  • 数组的元素可以是基本数据类型,也可以是引用数据类型。当元素是引用数据类型是,我们称为对象数组
  • 注意:对象数组,首先要创建数组对象本身,即确定数组的长度,然后再创建每一个元素对象,如果不创建,数组的元素的默认值就是null,所以很容易出现空指针异常NullPointerException
  • 定义格式,对象数组的定义格式同数组相同,只是数组类型为对象的类:
    类名[] = new 类名()
  • 对象数组的初始化
    • 对象数组的初始化也同数组相同,分类静态初始化和动态初始化,这里就不写了(偷个懒~),下面举个栗子就懂了
  • 示例代码:
    // 对象数组
    public class instanceArray{
    	public static void main(String[] args) {
    		// 定义对象数组,并动态初始化3个对象
    		Student[] stus = new Student[3];
    		// 实例化3个学生对象
    		Student s1 = new Student()
    		s1.name = "悟空";
    		s1.age = 1000;
    		Student s2 = new Student()
    		s2.name = "八戒";
    		s2.age = 800;
    		Student s3= new Student()
    		s3.name = "沙僧";
    		s3.age = 900;
    
    		// 将学生对象放入对象数组中
    		stus[0] = s1;
    		stus[1] = s2;
    		stus[2 = s3;
    
    		// 遍历数组
    		for (int i=0; i<stus.length; i++) {
    			Student s = stus[i];
    			s.showInfo()
    		}
    
    	}
    }
    
    class Student{
    	String name;
    	int age;
    
    	public void play(){
    		System.out.print("打妖怪...")
    	}
    
    	public void study(){
    		System.out.print("念经...")
    	}
    
    	public void showInfo(){
    		System.out.print("姓名: ":+name+",年龄:"+age+",正在"+study())
    	}
    }
    
  • 对象数组内存图分析:
    对象数组内存图分析

命令行参数

  • 这里提一个小功能,命令行参数,它一般就是可以在命令行(cmd)上传参数,让java可以接收这个参数,一般来说不怎么用到

  • 因为我以前做过爬虫,在主从服务器的时候,需要利用它将配置文件的参数传进去,好让服务器根据配置文件做不同的功能,这里了解一下就好,需要问问度娘就可以了

  • 命令行传参数,主要利用的是java的程序入口 main函数,还记得我们从开始的第一个程序吗:
    在这里插入图片描述
    当时没有提这里,这里就是命令行传参数用的,举个栗子:

     public class TestCommandParam{
        	//形参:String[] args
        	public static void main(String[] args){
        		System.out.println(args);
        		System.out.println(args.length);
        		
        		for(int i=0; i<args.length; i++){
        			System.out.println("第" + (i+1) + "个参数的值是:" + args[i]);
        		}
        	}
        }
    
    运行命令:
    java TestCommandParam
    java TestCommandParam 1 2 3
    java TestCommandParam hello atguigu
    

    在这里插入图片描述

总结

本章对面向对象的方法就介绍完毕啦,方法的细节点很多,多写写代码就好,不要死记硬背,在IT界,技术是通过有效代码的积累慢慢提高的,遇到了问题随时度娘,csdn,博客园等,90%的问题都可以解决。好了,下一章,我会对面向对象的三大特性进行分享,封装–继承–多态。欢迎大家后台吐槽~另外我后面的博客都用makedown写了,富文本编辑器是真的不好用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值