面向对象(基础)
6. 方法重载
基本介绍
java 中允许同一个类中,多个同名方法的存在,但要求形参列表不一致
比如:System.out.println(); out是 PrintStream 类型
重载的好处
- 减轻了起名的麻烦
- 减轻了记名的麻烦
public class OverLoad01 {
public static void main(String[] args) {
System.out.println(100);
System.out.println("Hello,World");
System.out.println('h');
System.out.println(1.1);
System.out.println(true);
}
}
练习
public class OverLoad02 {
public static void main(String[] args) {
MyCalculator mc = new MyCalculator();
System.out.println(mc.calculate(1, 2));
System.out.println(mc.calculate(1, 2.2));
System.out.println(mc.calculate(1.1, 2));
System.out.println(mc.calculate(1, 2, 3));
}
}
class MyCalculator {
//两个整数的和
public int calculate(int n1, int n2) {
System.out.println("public int calculate(int n1, int n2) 被调用");
return n1 + n2;
}
//一个整数,一个double的和
public double calculate(int n1, double n2) {
System.out.println("public int calculate(int n1, double n2) 被调用");
return n1 + n2;
}
//一个double,一个int的和
public double calculate(double n1, int n2) {
System.out.println("public int calculate(double n1, int n2) 被调用");
return n1 + n2;
}
//三个int的和
public int calculate(int n1, int n2, int n3) {
System.out.println("public int calculate(int n1, int n2, int n3) 被调用");
return n1 + n2 + n3;
}
}
注意事项和使用细节
- 方法名:必须相同
- 参数列表:必须不同(参数类型或个数或顺序,至少有一样不同,参数名无要求)
- 返回类型:无要求
方法重载练习
判断题:
- 与void show(int a,char b,double c){} 构成重载的有:【b、c、d、e、g】
a) void show(int x, char y, double z){}
b) int show(int a,double c.char b){}
c) void show(int a, double c, char b){}
d) boolean show(int c, char b){}
e) void show(double c){}
f) double show(int x, char y, double z){}
g) void shows(){}
程序题
- 编写程序,类Methods中定义三个重载方法并调用。
方法名为m。三个方法分别接收一个int参数、两个int参数、一个字符串参数。
分别执行平方运算并输出结果,相乘并输出结果,输出字符串信息。
在主类的main ()方法中分别用参数区别调用三个方法。
public class OverLoad03 {
public static void main(String[] args) {
Methods method = new Methods();
method.m(10);
method.m(10,10);
method.m("hello world");
}
}
/**
* 1. 编写程序,类Methods中定义三个重载方法并调用。
* 方法名为m。三个方法分别接收一个int参数、两个int参数、一个字符串参数
。
* 分别执行平方运算并输出结果,相乘并输出结果,输出字符串信息。
* 在主类的main ()方法中分别用参数区别调用三个方法。
* */
class Methods {
public void m(int n) {
System.out.println("平方 = " + (n * n));
}
public void m(int n1, int n2) {
System.out.println("相乘 = " + (n1 * n2));
}
public void m(String str) {
System.out.println("传入的 str = " + str);
}
}
- 在Methods类,定义三个重载方法max(),
第一个方法,返回两个int值中的最大值,
第二个方法,返回两个double值中的最大值,
第三个方法,返回三个double值中的最大值,
并分别调用三个方法。
public class OverLoad04 {
public static void main(String[] args) {
Methods method = new Methods();
System.out.println(method.max(10, 20));
System.out.println(method.max(1.1, 2.2));
System.out.println(method.max(1.1, 2.2, 3.3));
}
}
/**
* 2. 在Methods类,定义三个重载方法max(),
* 第一个方法,返回两个int值中的最大值,
* 第二个方法,返回两个double值中的最大值,
* 第三个方法,返回三个double值中的最大值,
* 并分别调用三个方法。
*
* */
class Methods {
public int max(int n1, int n2) {
return n1 > n2 ? n1 : n2;
}
public double max(double n1, double n2) {
return n1 > n2 ? n1 : n2;
}
public double max(double n1, double n2, double n3) {
double max = n1 > n2 ? n1 : n2;
return max > n3 ? max : n3;
}
}
7. 可变参数
- 基本概念
java允许将同一个类中 多个同名 同功能 但 参数个数不同 的方法,封装成一个方法。
- 基本语法
访问修饰符 返回类型 方法名(数据类型... 形参名) {
}
public class VarParameter01 {
public static void main(String[] args) {
Method method = new Method();
System.out.println(method.sum(1, 5, 100));
}
}
class Method {
//可以计算 2个数的和,3个数的和,4,5...
//可以使用方法重载
//public int sum(int n1, int n2) { //2个数的和
// return n1 + n2;
//}
//public int sum(int n1, int n2, int n3) { //3个数的和
// return n1 + n2 + n3;
//}
//public int sum(int n1, int n2, int n3, int n4) { //4个数的和
// return n1 + n2 + n3 + n4;
//}
//...
//上面的三个方法名称相同,功能相同,参数个数不同 -> 使用可变参数优化
//1. int... 表示接受的是可变参数,类型是int,即可以接收多个int(0-多)
//2. 使用可变参数时,可以当作数组来使用,即 nums 可以当作数组
//3. 遍历 nums 求和即可
public int sum(int... nums) {
//System.out.println("接收的参数个数=" + nums.length);
int res = 0;
for (int i = 0; i < nums.length; i++) {
res += num[i];
}
return res;
}
}
- 注意事项和使用细节
- 可变参数的实参可以为0个或任意多个。
- 可变参数的实参可以为数组。
- 可变参数的本质就是数组。
- 可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
- 一个形参列表中只能出现一个可变参数
public class VarParameter02 {
public static void main(String[] args) {
//细节:可变参数的实参可以为数组
int[] arr = {1,2,3};
T t1 = new T();
t1.f1(arr);
}
}
class T {
public void f1(int... nums) {
System.out.println("长度=" + nums.length);
}
//可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
//public void f2(double... nums, String str) {} //编译出错,因为可变参数没放在其他类型的参数后面
//
//编译没出错
public void f2(String str, double... nums) {
}
//细节:一个形参列表中只能出现一个可变参数
//
//public void f3(int... nums1, double... nums2) {} //编译报错
//
//编译没报错
public void f3(int... nums){}
}
可变参数练习
有三个方法,分别实现返回姓名和两门课成绩(总分),返回姓名和三门课成绩(总分),返回姓名和五门课成绩(总分)。
封装成一个可变参数的方法。
public class VarParameter03 {
public static void main(String[] args) {
Method m = new Method();
System.out.println(m.showScore("milan", 90.1, 80.0));
System.out.println(m.showScore("terry", 90.1, 80.0, 10, 30.5, 70));
}
}
class Method {
/*
* 有三个方法,
* 分别实现返回姓名和两门课成绩(总分),
* 返回姓名和三门课成绩(总分),
* 返回姓名和五门课成绩(总分)。
* 封装成一个可变参数的方法。
* */
public String showScore(String name, double... scores) {
double totalScore = 0;
for (int i = 0; i < scores.length; i++) {
totalScore += scores[i];
}
return name + " 有 " + scores.length + "门课的成绩总分为=" + totalScore;
}
}
8. 作用域
基本介绍
- 在 java 编程中,主要的变量就是属性(成员变量)和局部变量。
- 局部变量一般是指在成员方法中定义的变量。
- java 中作用域的分类
- 全局变量:也就是属性,作用域为整个类体。Cat类: cry eat等方法使用属性。
- 局部变量:也就是除了属性之外的其他变量,作用域为定义它的代码块中!
- 全局变量可以不赋值,直接使用,因为有默认值,局部变量必须赋值后,才能使用,因为没有默认值。
public class VarScope01 {
public static void main(String[] args) {
}
}
class Cat {
//全局变量:也就是属性,作用域为整个类体 Cat类:cry eat 等方法使用属性
//属性在定义时,可以直接赋值
int age = 10; //指定的值是 10
//全局变量可以不赋值,直接使用,因为有默认值。
double weight; //默认值是0.0
//局部变量必须赋值后,才能使用,因为没有默认值。
public void hi() {
//int num; //全局变量不赋值,编译时报错
int num = 1;
//String address; //全局变量不赋值,编译时报错
String addrees = "外面的猫";
System.out.println("num=" + num);
System.out.println("addrees=" + addrees);
System.out.println("weight=" + weight);
}
public void cry() {
//1.局部变量一般是指在成员方法中定义的变量
//2.n 和 name 就是局部变量
//3.n 和 name 的作用域在 cry方法中
int n = 10;
String name = "jack";
System.out.println("在 cry 中使用属性 age = " + age); //没报错
}
public void eat() {
System.out.println("在 eat 中使用属性 age = " + age); //没报错
//System.out.println("在 eat 中使用属性 cry = " + cry); //报错
}
}
注意事项和细节使用
-
属性和局部变量可以重名,访问时遵循就近原则。
-
在同一个作用域中,比如在同一个成员方法中,两个局部变量,不能重名。
-
属性生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁。
-
局部变量,生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而销毁。即在一次方法调用过程中。
public class VarScopr02 {
public static void main(String[] args) {
Person p1 = new Person();
p1.say();
//当执行say方法时,say方法的局部变量比如name,会创建,当say执行完毕后
//name局部变量就销毁,但是属性(全局变量)仍然可以使用
}
}
class Person {
String name = "jack";
public void say() {
//细节:属性和局部变量可以重名,访问时遵循就近原则
String name = "king"; System.out.println("say() name = " + name); //输出的是king
}
public void hi() {
String address = "北京";
//String address = "上海"; //重复定义
String name = "tom"; //可以
}
}
- 作用域范围不同
- 全局变量/属性:可以被本类使用,或其他类使用(通过对象调用)
- 局部变量:只能在本类中对应的方法中使用
- 修饰符不同
- 全局变量/属性可以加修饰符
- 局部变量不可以加修饰符
public class VarScope03 {
public static void main(String[] args) {
T t1 = new T();
t1.test(); //jack //第一种跨类访问对象属性的方式
Person p1 = new Person();
t1.test2(p1); //jack //第二种跨类访问对象属性的方式
}
}
class T {
//全局变量/属性可以加修饰符(public protected private ... )
private int age = 20;
//全局变量/属性:可以被本类使用,或其他类使用(通过对象调用)
public void test() {
Person p1 = new Person();
System.out.println(p1.name); //jack
}
public void test2(Person p) {
System.out.println(p.name); //jack
}
}
class Person {
String name = "jack";
public void say() {
//局部变量不可以加修饰符
//public String name = "king"; //报错
String name = "king";
}
}
9. 构造方法/构造器
基本语法
[修饰符] 方法名 (形参列表) {
方法体;
}
- 构造器的修饰符可以默认,也可以是 public protected private …
- 构造器没有返回值
- 方法名和类名字必须一样
- 参数列表和成员方法一样的规则
- 构造器的调用系统完成
基本介绍
构造方法又叫构造器(constructor),是类的一种特殊的方法,它的主要作用是完成对新对象的初始化。
构造方法的特点:
- 方法名和类名相同
- 没有返回值
- 在创建对象时,系统会自动的调用该类的构造器完成对对象的初始化。
public class Constructor01 {
public static void main(String[] args) {
Person p1 = new Person("smith",80);
System.out.println("p1信息如下");
System.out.println("p1对象 name = " + p1.name);
System.out.println("p1对象 age = " + p1.age);
}
}
//在创建人类的对象时.就直接指定这个对象的年龄和姓名
class Person {
String name;
int age;
//构造器
//1. 构造器没有返回值,也不能void
//2. 构造器的名称和类Person—样
///3. (String pName, int pAge) 是构造器形参列表,规则和成员方法一样
public Person(String pName, int pAge) {
System.out.println("构造器被调用,完成对象属性初始化");
name = pName;
age = pAge;
}
}
注意事项和使用细节
- —个类可以定义多个不同的构造器,即构造器重载
- 比如:我们可以再给 Person 类定义一个构造器,用来创建对象的时候,只指定人名,不需要指定年龄
- 构造器名和类名要相同
- 构造器没有返回值
- 构造器是完成对象的初始化并不是创建对象
- 在创建对象时,系统自动的调用该类的构造方法
public class Constructor02 {
public static void main(String[] args) { //—个类可以定义多个不同的构造器,即构造器重载
Person p1 = new Person("king",40); //第一个构造器
Person p2 = new Person("tom"); //第二个构造器
}
}
class Person {
String name;
int age;
//第一个构造器
public Person(String pName, int pAge) {
name = pName;
age = pAge;
}
//第二个构造器,至指定人名,不需要指定年龄
public Person(String pName) {
name = pName;
}
//构造器名和类名要相同
//构造器没有返回值
//public P(String pName) {} //报错,因为系统认为是一个方法,而不>是构造器,方法需要返回值
}
- 如果程序员没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫默认构造器),比如 Dog() {}
public class Constructor03 {
public static void main(String[] args) {
Dog d = new Dog();
}
}
class Dog {
//如果程序员没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫
默认构造器)
/**
* 默认构造器
* Dog() {
*
* }
*
* */
}
使用 javap 指令反编译查看 Dog 类
- 一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,除非显式的定义一下,即:Dog() {}
public class Constructor03 {
public static void main(String[] args) {
Dog d = new Dog(); //定义自己的构造器后使用默认的无参构造器,系统会报错
//除非显式定义 Dog(){}
}
}
class Dog {
//如果程序员没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫
默认构造器)
/**
* 默认构造器
* Dog() {
*
* }
*
* */
//一旦定义了自己的构造器,默认的构造器就覆盖了,
//就不能再使用默认的无参构造器,除非显式的定义一下
public Dog(String dName) {
//...
}
Dog() { //显式的定义一下
}
}
使用javap指令反编译看看
练习
public class Constructor04 {
public static void main(String[] args) {
Person p1 = new Person(); //无参构造器
System.out.println("p1 的信息 name = " + p1.name); //null
System.out.println("p1 的信息 age = " + p1.age); //18
Person p2 = new Person("tom", 20); //带参数的构造器
System.out.println("p2 的信息 name = " + p2.name); //null
System.out.println("p2 的信息 age = " + p2.age); //18
}
}
/**
* 在前面定义的Person类中添加两个构造器:
* 第一个无参构造器:利用构造器设置所有人的age属性初始值都为18
* 第二个带pName和pAge两个参数的构造器:
* 使得每次创建Person对象的同时初始化对象的age属性值和name属性值。
* 分别使用不同的构造器,创建对象.
*
* */
class Person {
String name;
int age;
//第一个无参构造器:利用构造器设置所有人的age属性初始值都为18
public Person() {
age = 18;
}
//第二个带pName和pAge两个参数的构造器
public Person(String pName, int pAge) {
name = pName;
age = pAge;
}
}
10. 对象创建的流程分析
class Person { //类Person
String name;
int age;
public Person(String pName, int pAge) { //构造器
name = n;//给属性赋值
age = a;
}
}
Person p = new Person("tom",20);
流程分析
- 加载 Person 类信息(Person.class),只会加载一次
- 在堆中分配空间(地址)
- 完成对象初始化
- 默认初始化 age = 0 name = null
- 显式初始化 age = 90,name = null
- 构造器的初始化 age = 20,name = tom
- 把对象在堆中的地址,返回给p(p是对象名,也可以理解是对象的引用)
11. this 关键字
11.1 引出this
public class This01 {
public static void main(String[] args) {
Dog dog1 = new Dog("大壮", 3);
dog1.info();
//输出的不是 大壮 和 3 ,而是 null 和 0
}
}
class Dog { //类
String name;
int age;
//public Dog(String dName, int dAge) { //构造器
// name = dName;
// age = dAge;
//}
//
// 如果我们构造器的形参,能够直接写成属性名,就更好了
// 但是出现了一个问题,根据变量的作用域原则
// 构造器的 name 是局部变量,而不是属性
// 构造器的 age 是局部变量,而不是属性
// ==> 引出this关键字来解决
public Dog(String name, int age) { //构造器
name = name; //null
age = age; //0
}
public void info() { //成员方法,输出属性 * 信息
System.out.println(name + "\t" + age + "\t");
}
}
11.2 什么是 this
java虚拟机会给每个对象分配this,代表当前对象。
public class This01 {
public static void main(String[] args) {
//Dog dog1 = new Dog("大壮", 3);
//dog1.info();
//输出的不是 大壮 和 3 ,而是 null 和 0
//
Dog dog1 = new Dog("大壮", 3);
dog1.info();
//输出的是 大壮 和 3
}
}
class Dog { //类
String name;
int age;
//public Dog(String dName, int dAge) { //构造器
// name = dName;
// age = dAge;
//}
//
// 如果我们构造器的形参,能够直接写成属性名,就更好了
// 但是出现了一个问题,根据变量的作用域原则
// 构造器的 name 是局部变量,而不是属性
// 构造器的 age 是局部变量,而不是属性
// ==> 引出this关键字来解决
//
//public Dog(String name, int age) { //构造器
// name = name; //null
// age = age; //0
//}
public Dog(String name, int age) { //构造器
//this.name 就是当前对象的属性 name
this.name = name;
//this.age 就是当前对象的属性 age
this.age = age;
}
public void info() { //成员方法,输出属性 * 信息
System.out.println(name + "\t" + age + "\t");
}
}
this 的本质
哪个对象调用,this就代表哪个对象
public class This02 {
public static void main(String[] args) {
Dog dog1 = new Dog("大壮", 3);
dog1.info();
System.out.println("main:dog1.hashCode是:" + dog1.hashCode()); //使用hashCode() 查看对象的情况
}
}
class Dog { //类
String name;
int age;
public Dog(String name, int age) { //构造器
//this.name 就是当前对象的属性 name
this.name = name;
//this.age 就是当前对象的属性 age
this.age = age;
System.out.println("构造器:this.hashCode是:" + this.hashCode()); //使用hashCode() 查看对象的情况
}
public void info() {
System.out.println("info:this.hashCode是:" + this.hashCode()); //使用hashCode() 查看对象的情况
}
}
11.3 this的注意事项和使用细节
- this 关键字可以用来访问本类的属性、方法、构造器
- this用于区分当前类的属性和局部变量
- 访问成员方法的语法:this.方法名(参数列表);
- 访问构造器语法:this(参数列表); 注意只能在构造器中使用this(即只能在构造器中访问另外一个构造器)
- 注意:访问构造器语法:this(参数列表); 必须放置第一条语句
- 不能在类定义的外部使用,只能在类定义的方法中使用。
public class This03 {
public static void main(String[] args) {
System.out.println("f2()方法 去调用本类的 f1() 方法");
T t1 = new T();
t1.f2();
System.out.println("构造器 去访问另一个构造器");
T t2 = new T();
System.out.println("访问属性");
T t3 = new T();
t3.f3();
}
}
class T {
String name = "tom";
int age = 10;
/**
* 访问构造器语法:this(参数列表);
* 注意只能在构造器中使用this
* (即只能在构造器中访问另外一个构造器)
*
* 注意:访问构造器语法:this(参数列表); 必须放置第一条语句
*
* */
public T() {
//这里去访问 T(String name, int age)
this("jack", 100);
System.out.println("T() 构造器");
}
public T(String name, int age) {
System.out.println("T(String name, int age) 构造器");
}
//访问成员方法的语法:this.方法名(参数列表);
public void f1() {
System.out.println("f1() 方法...");
}
public void f2() {
System.out.println("f2() 方法...");
//调用本类的 f1
//第一种方式
f1();
//第二种方式
this.f1();
}
public void f3() {
//使用传统方式访问属性
System.out.println("name = " + name + " age = " + age);
//使用this访问属性
System.out.println("name = " + this.name + " age = " + this.age);
}
}
11.4 this 练习
public class This04 {
public static void main(String[] args) {
Person p1 = new Person("mary", 20);
Person p2 = new Person("smith", 30);
System.out.println("p1 和 p2 比较的结果 = " + p1.compareTo(p2));
}
}
/**
* 定义Person类,里面有name、age属性,
* 并提供compareTo比较方法,用于判断是否和另一个人相等,
* 提供测试类TestPerson用于测试,
* 名字和年龄完全一样,就返回true,否则返回false
*
* */
class Person {
String name;
int age;
//构造器
public Person(String name, int age) {
this.name = name;
this.age = age;
}
//compareTo 比较方法
public boolean compareTo(Person p) {
//名字和年龄完全一样
//if (this.name.equals(p.name) && this.age == p.age) {
// return true;
//} else {
// return false;
//}
//
//简化代码
return this.name.equals(p.name) && this.age == p.age;
}
}