Java - 类和方法

本文详细探讨了Java中的类和方法,包括重载方法、构造函数、对象作为形参、实参传递、访问控制、static关键字、final属性、嵌套类和内部类、String类的用法、命令行参数传递、varargs以及处理重载和模糊性的最佳实践。
摘要由CSDN通过智能技术生成

类和方法的深入分析

1. 重载方法

  • 在一个类中,定义两个或多个共享相同名称、但形参不同的方法
class OverloadedDemo {
    void test() {
        System.out.println("无形参");
    }

    void test(int a) {
        System.out.println("a: " + a);
    }

    void test(int a, int b) {
        System.out.println("a and b: " + a + " " + b);
    }

    double test(double a) {
        System.out.println("double a: " + a);
        return a*a;
    }
}

class Overload {
    public static void main(String[] args) {
        OverloadedDemo ob = new OverloadedDemo();
        double result;

        ob.test();
        ob.test(10);
        ob.test(10, 20);
        result = ob.test(123.25);
        System.out.println("Result of ob.test(123.25): " + result);
    }
}

方法重载的规则:

  • 方法名称必须相同
  • 参数列表必须不同(个数不同、或类型不同、参数排列顺序不同等)
  • 方法的返回类型可以相同也可以不相同
  • 仅仅返回类型不同不足以称为方法的重载

  • 如果类中使用 int 实参调用 test() 方法,但没有定义 test(int),Java 将会使用自动类型转换(比如若定义了 test(double),则将 int 转换为 double

  • 方法重载支持多态,Java 实现 “一个接口,多个方法” 机制


重载构造函数

class Box {
    double width;
    double height;
    double depth;

    Box(double w, double h, double d) {
        width = w;
        height = h;
        depth = d;
    }

    // 当使用者不需要知道 Box 的尺寸时
    Box() {
        width = -1;
        height = -1;
        depth = -1;
    }

    // 当使用者的 Box 为正方体时
    Box(double len) {
        width = height = depth = len;
    }

    double volume() {
        return width * height * depth;
    }
}

class OverloadCons {
    public static void main(String[] args) {
        Box mybox1 = new Box(10, 20, 15);
        Box mybox2 = new Box();
        Box mycube = new Box(7);

        double vol;

        vol = mybox1.volume();
        System.out.println("mybox1 的体积为: " + vol);

        vol = mybox2.volume();
        System.out.println("mybox2 的体积为: " + vol);

        vol = mycube.volume();
        System.out.println("mybox3 的体积为: " + vol);
    }
}

2. 将对象用作形参

class Test {
    int a, b;

    Test(int i, int j) {
        a = i;
        b = j;
    }

    boolean equalTo(Test o) {
        if (o.a == a && o.b == b) return true;
        else return false;
    }
}

class PassOb {
    public static void main(String[] args) {
        Test ob1 = new Test(100, 20);
        Test ob2 = new Test(100, 20);
        Test ob3 = new Test(-1, -1);

        System.out.println("ob1 == ob2: " + ob1.equalTo(ob2));
        System.out.println("ob1 == ob3: " + ob1.equalTo(ob3));
    }
}

对象形参最常用于调用构造函数,经常需要创建在开始时就与已经存在的某些对象相同的对象

需要定义一个以类的对象作为形参的构造函数

class Box {
    double width;
    double height;
    double depth;

    // 将对象用作形参
    Box(Box ob) {
        width = ob.width;
        height = ob.height;
        depth = ob.depth;
    }

    Box(double w, double h, double d) {
        width = w;
        height = h;
        depth = d;
    }

    double volume() {
        return width * height * depth;
    }
}

class OverloadCons {
    public static void main(String[] args) {
        Box mybox = new Box(10, 20, 15);
        Box myclone = new Box(mybox);

        double vol;

        vol = mybox.volume();
        System.out.println("mybox 的体积为: " + vol);

        vol = myclone.volume();
        System.out.println("clone 的体积为: " + vol);
    }
}

3. 实参传递的深入分析

  • 向子例程传递实参的两种方式:

    • call-by-value:按值调用

      • 为方法传递基本类型(int, double等)时,使用按值传递,方法内部对形参的操作不会影响方法外部的实参的值。
    • call-by-reference:按引用调用

      • 为方法传递对象时,使用按引用调用。**当创建类类型变量时,只是创建指向对象的引用。**因此,当将引用传递给方法时,接收引用的形参所引用的对象与实参引用的是同一个对象。在方法内修改对象,将影响到作为实参的对象。
      class Test {
          int a, b;
      
          Test(int i, int j) {
              a = i;
              b = j;
          }
      
          void meth(Test o) {
              o.a *= 2;
              o.b /= 2;
          }
      }
      
      class PassObjRef {
          public static void main(String[] args) {
              Test ob = new Test(15, 20);
      
              System.out.println("ob.a, ob.b 调用方法前的值为: " + ob.a + " " + ob.b);
      
              ob.meth(ob);
      
              System.out.println("ob.a, ob.b 调用方法后的值为: " + ob.a + " " + ob.b);
          }
      }
      

当将对象引用传递给方法时,引用本身是使用按值调用的方式传递的。但是,由于传递的值引用一个对象,值的副本仍然引用相应实参指向的同一个对象

4. 返回对象

class Test {
    int a;

    Test(int i) {
        a = i;
    }

    Test incrByTen() {
        Test temp = new Test(a+10);
        return temp;
    }
}

class RetOb {
    public static void main(String[] args) {
        Test ob1 = new Test(2);
        Test ob2;

        ob2 = ob1.incrByTen();
        System.out.println("ob1.a: " + ob1.a);
        System.out.println("ob2.a: " + ob2.a);

        ob2 = ob2.incrByTen();
        System.out.println("ob2.a second time: " + ob2.a);
    }
}

以上,所有对象都是使用 new 运算符动态创建的,不必担心对象会由于其创建时所在的方法结束而超出作用域。

5. 访问控制(access control)

  • 访问修饰符(access modifier)

    • public
      • 该成员可以被任何代码访问
    • private
      • 该成员只能被所属类的其他成员访问
    • protected

    如果没有使用访问修饰符,该类成员在自己的包中默认时共有的,但在包外不能访问。

class Test {
    int a;
    public int b;
    // 私有
    private int c;

    void setc(int i) {
        c = i;
    }
    int getc() {
        return c;
    }
}

class AccessTest {
    public static void main(String[] args) {
        Test ob = new Test();

        ob.a = 10;
        ob.b = 20;

        // ob.c = 100;   // 报错! c为私有, 必须通过共有方法访问
        ob.setc(100);
        System.out.println("a, b and c: " + ob.a + " " + ob.b + " " + ob.getc());
    }
}

堆栈类实例:

class Stack {
    // 标识为私有
    // 如果不通过 push() 和 pop() 方法,就不能访问和修改它们
    private int[] stck = new int[10];
    private int tos;
    
    Stack() {
        tos = -1;
    }
    
    void push(int item) {
        if (tos==9)
            System.out.println("Stack is full.");
        else
            stck[++tos] = item;
    }
    
    int pop() {
        if (tos<0) {
            System.out.println("Stack underflow.");
            return 0;
        }
        else
            return stck[tos--];
    }
}

6. static 关键字

如果成员被声明为静态(static)的,则可以在创建类的任何对象之前访问该成员,不需要使用任何对象引用。

被声明为静态的实例变量在本质上是全局变量。当声明类的对象时,不会生成静态变量的副本。相反,类的所有实例共享相同的静态变量。

  • 被声明为静态的方法存在几个限制:
    • 只能直接调用其类的其他静态方法
    • 只能直接访问其类的静态变量
    • 不能以任何方式引用 this 或 super 关键字
class UseStatic {
    // 1. a 被设置成 3
    static int a = 3;
    static int b;

    // 4. 调用 meth() 方法
    static void meth(int x) {
        System.out.println("x = " + x);
        System.out.println("a = " + a);
        System.out.println("b = " + b);
    }

    // 2. 执行静态代码块
    // 输出一条消息,并将 b 初始化为 a*4
    static {
        System.out.println("静态块初始化...");
        b = a * 4;
    }

    // 3. 调用 main() 方法
    public static void main(String[] args) {
        meth(42);
    }
}
Output:
静态块初始化...
x = 42
a = 3
b = 12
  • 在定义静态方法和静态变量的类的外部,可以独立使用这些静态成员,不必依赖任何对象。如果希望在类的外部访问静态方法 / 静态变量:
    类名.方法名()
    类名.变量名

  1. 静态变量和非静态变量对比
public class Student {
    private static int age;    // 静态的变量
    private double score;    // 非静态的变量

    public static void main(String[] args) {
        Student s1 = new Student();
        
        System.out.println(Student.age);
        // System.out.println(Student.score);    // 编译报错,非静态变量
        System.out.println(s1.age);
        System.out.println(s1.score);
    }
}

  1. 执行顺序
public class Person {
    // 2. 赋初值
    {
        // 匿名代码块
        System.out.println("匿名代码块");
    }

    // 1. 静态代码块 只执行一次
    static {
        System.out.println("静态代码块");
    }

    // 3. 构造方法
    public Person() {
        System.out.println("构造方法");
    }

    public static void main(String[] args) {
        Person person = new Person();
        System.out.println("================");
        Person person2 = new Person();
    }
}
Output:
静态代码块
匿名代码块
构造方法
================
匿名代码块
构造方法

  1. 静态导入包
import static java.lang.Math.random;
import static java.lang.Math.PI;

public class Test {
    public static void main(String[] args) {
        System.out.println(random());
        System.out.println(PI);
    }
}

7. final

  • 防止变量的内容被修改,本质上就是将变量变成常量**(final 之后断子绝孙)**

  • final 变量必须在声明时进行初始化

    1. 在声明式为其提供一个值
      final int FILE_NEW = 1;
    2. 在构造函数中为其赋值
  • 编码约定:final 变量名全部大写

  • 方法参数局部变量声明为 final:

    • 方法参数:防止在方法中修改参数
    • 局部变量:防止多次为其赋值

8. 嵌套类和内部类

嵌套类 (nested class) :在类的内部定义另一个类。作用域被限制在包含它的类中。

如果类 B 是在类 A 中定义的,那么类 B 不能独立于类 A 而存在

嵌套类可以访问包含它的类的成员,包括私有成员;封闭类(包含嵌套类的类)不能访问嵌套类的成员;嵌套类可作为封闭类的成员直接在封闭类中声明,也可以在代码块中声明。可以在任何代码块的作用域内定义内部类:

class Outer {
    int outer_x = 100;
    
    void test() {
        // 嵌套类可作为封闭类的成员直接在封闭类中声明
        Inner inner = new Inner();
        inner.display();
        
        // 在 for 循环内定义嵌套类
        for (int i = 0; i < 10; i++) {
            class Innerinner {
                void inDisplay() {
                    System.out.println("inDisplay: outer_x = " + outer_x);
                }
            }
            Innerinner innerInner = new Innerinner();
            innerInner.inDisplay();
        }
    }
    
    class Inner {
        int y = 10;   // 嵌套类 Inner 的成员
        
        // 嵌套类访问包含它的类的成员
        void display() {
            System.out.println("display: outer_x = " + outer_x);
        }
    }
    
    void showy() {
        // 封闭类不能访问嵌套类的成员
        // System.out.println(y);
    }
}

public class InnerClassDemo {
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.test();
    }
}

9. String 类

str1.equals(str2);  // 测试两个字符串是否相等
str.length();   // 获取字符串长度
str.charAt(1);   // 获取字符串中指定索引处的字符

10. 命令行传参

package com.feiye.CommandLine;

class CommandLine {
    public static void main(String[] args) {
        for (int i = 0; i < args.length; i++) {
            System.out.println("args[" + i + "]: " + args[i]);
        }
    }
}
  1. 在命令行中,该文件的路径下编译 javac 文件:

    javac CommandLine.java
    
  2. 返回最上一层路径(src),通过命令行向 main() 函数传参:

    java com.feiye.CommandLine.CommandLine this is a test 100 -1
    
  3. 输出结果:

    args[0]: this
    args[1]: is
    args[2]: a
    args[3]: test
    args[4]: 100
    args[5]: -1
    

在这里插入图片描述

所有命令行参数都是作为字符串传递的。必须手动将数值转换成它们的内部形式。

11. varargs: 可变长度实参

  • 通过数组传参

    class PassArray {
        static void vaTest(int[] v) {
            System.out.print("数组长度:" + v.length + " 内容:");
    
            for (int x: v)
                System.out.print(x + " ");
            System.out.println();
        }
    
        public static void main(String[] args) {
            int[] n1 = {10};
            int[] n2 = {1,2,3};
            int[] n3 = {};
    
            vaTest(n1);
            vaTest(n2);
            vaTest(n3);
        }
    }
    
  • 可变长度实参

    class VarArgs {
        static void vaTest(int ... v) {
            System.out.print("参数长度:" + v.length + " 内容:");
    
            for (int x : v)
                System.out.print(x + " ");
            System.out.println();
        }
    
        public static void main(String[] args) {
            vaTest(10);
            vaTest(1,2,3);
            vaTest();
        }
    }
    

  1. 可变长度形参必须是方法最后声明的形参
  2. 一个方法中只能有一个可变长度形参

11.1 重载 varargs 方法

若一个类中有两个方法:

vaTest(int ... v);
vaTest(int x);

当传递一个 int 类型实参调用 vaTest(int x);;传递多个 int 类型实参时调用 vaTest(int ... v);

11.2 varargs 方法与模糊性

  1. 若一个类中存在两个方法:
    vaTest(int ... v);
    vaTest(boolean ... v);
    当程序中调用 vaTest(); 时,编译不通过
  2. 若一个类中存在两个方法:
    vaTest(int ... v);
    vaTest(int n, int ... v);
    当程序中调用 vaTest(1); 时,编译不通过

综上,在某些情况下,有时需要放弃重载,简单地使用两个不同的方法名。

  • 22
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值