一、类与对象
二、成员方法
三、成员方法传参机制
四、重载(overload)
五、可变参数
1.基本概念
Java允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法。
2.基本语法
访问修饰符 返回类型 方法名(数据类型... 形参名){
}
3.入门案例
可以计算2个数的和,3个数的和,4,5....
public class VarParameter01 {
public static void main(String[] args) {
HspMethod m = new HspMethod();
System.out.println(m.sum(10, 20));//30
System.out.println(m.sum(10, 20, 30));//60
System.out.println(m.sum(10, 20, 30, 40));//100
}
}
class HspMethod {
// //1.使用方法重载
// 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相当于数组
public int sum(int... nums) {
System.out.println("传入的参数个数是:" + nums.length);
int res = 0;
for(int i = 0; i < nums.length; i++) {
res += nums[i];
}
return res;
}
}
4.注意事项
(1)可变参数的实参可以为0个或任意多个。
(2)可变参数的实参可以为数组。
(3)可变参数的本质就是数组。
(4)可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后。
(5)一个形参列表中只能出现一个可变参数。
public class VarParameterDetail {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
T t = new T();
t.f1(arr);
}
}
class T {
//(2)可变参数的实参可以为数组。
public void f1(int... nums) {
System.out.println("长度=" + nums.length);//3
}
//(4)可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后。
public void f2(String str, int... nums) {
}
//(5)一个形参列表中只能出现一个可变参数。
//下面的写法是错误的.
// public void f3(int... nums1, double... nums2) {
// }
}
5.课堂练习
有三个方法,分别实现返回姓名和两门课成绩(总分),返回姓名和三门课成绩(总分),返回姓名和五门课成绩(总分)。封装成一个可变参数的方法。
public class VarParameterExercise {
public static void main(String[] args) {
HspMethod t = new HspMethod();
System.out.println(t.showScore("milan", 40.1, 11.9));
System.out.println(t.showScore("terry", 40.1, 11.9, 10.17, 10.25));
}
}
class HspMethod {
//返回多个数据类型就用String
public String showScore(String name, double... scores) {
double totalScores = 0;
for(int i = 0; i < scores.length; i++) {
totalScores += scores[i];
}
return name + "有" + scores.length + "门成绩总分为" + totalScores;
}
}
六、作用域(非常重要)
1.基本使用
(1)在java编程中,主要的变量就是属性(成员变量)和局部变量。
(2)我们说的局部变量一般是指在成员方法中定义的变量。
(3)java中作用域的分类
全局变量:也就是属性,作用域为整个类体。
局部变量:也就是除了属性之外的其他变量,作用域为定义它的代码块。
(4)全局变量(属性)可以不赋值,直接使用,因为有默认值;局部变量必须赋值后才能使用,因为没有默认值。
public class VarScope {
public static void main(String[] args) {
}
}
class Cat {
//全局变量:也就是属性,作用域为整个类体。
int age = 10; //属性在定义时,可以直接赋值。
//全局变量(属性)可以不赋值,直接使用,因为有默认值;
double weight; //默认值是0.0
//局部变量必须赋值后才能使用,因为没有默认值。
public void hi() {
int i = 1;
String address;
System.out.println("i=" + i);
//System.out.println("address=" + address); //错误:可能尚未初始化变量address
System.out.println("weight=" + weight);
}
{
int num = 100; //代码块中的变量
}
public void cry() {
//1.我们说的局部变量一般是指在成员方法中定义的变量。
//2.n 和 name 就是所说的成员变量。
//3.n 和 name 的作用域在cry方法中。
int n = 0;
String name = "xiaolin";
System.out.println("在eat方法中使用属性 age=" + age);
}
public void eat() {
System.out.println("在eat方法中使用属性 age=" + age);
//System.out.println("在eat方法中使用cry的变量 name=" + name); //错误
//System.out.println("在eat方法中使用代码块中的变量 num=" + num); //错误
}
}
2.注意事项和使用细节
(1)属性和局部变量可以重名,访问时遵循就近原则。
(2)在同一个作用域中,比如在同一个成员方法中,两个局部变量,不能重名。
(3)属性生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁;
局部变量生命周期短,伴随着它所在代码块的执行而创建,伴随着代码块的结束而销毁,即在一次方法调用过程中。
(4)作用域范围不同
全局变量/属性:可以被本类使用,或其他类使用(通过对象调用,有两种办法)。
局部变量:只能在本类中对应的方法中使用。
(5)修饰符不同
全局变量/属性可以加修饰符。
局部变量不可以加修饰符。
public class VarScopeDetail {
public static void main(String[] args) {
Person p1 = new Person();
//3.属性生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁;
//局部变量生命周期短,伴随着它所在代码块的执行而创建,伴随着代码块的结束而销毁,即在一次方法调用过程中。
p1.say();
//当执行say方法时,say方法的局部变量(比如name)会创建,当say执行完毕后,name局部变量就销毁,但是属性(全局变量)仍然可以使用
T t1 = new T();
t1.test(); //第1种跨类访问对象属性的方式
t1.test2(p1); //第2种跨类访问对象属性的方式
}
}
class T {
//5.属性可以加修饰符(protected public private...)
public int age = 10;//正确
//4.第1种跨类访问对象属性的方式
public void test() {
//5.局部变量不能加修饰符
//public int age = 10;//错误
Person p = new Person();
System.out.println(p.name); //jack
}
//4.第2种跨类访问对象属性的方式:将别的类创建的对象作为参数传入该方法
public void test2(Person p) {
System.out.println(p.name); //jack
}
}
class Person {
String name = "jack";
public void say() {
//1.属性和局部变量可以重名,访问时遵循就近原则。
String name = "king";
System.out.println("say() name=" + name);//king
}
public void hi() {
//2.在同一个作用域中,比如在同一个成员方法中,两个局部变量,不能重名。
String address = "北京";
//String address = "上海";//错误,重复定义变量
}
}
七、构造器
1.需求
前面我们在创建人类的对象时,是先把一个对象创建好后,再给他的年龄和姓名属性赋值。如果现在我要求:在创建人类的对象时,就直接指定对象的年龄和姓名,该怎么做?这时就可以使用构造器。
2.基本介绍
构造方法又叫构造器(constructor),是类的一种特殊的方法。它的主要作用是完成对象的属性初始化。(而不是创建对象)
3.基本语法
[修饰符] 方法名(形参列表){
方法体;
}
特点:(1)构造器的修饰符可以默认,也可以是public\protected\private.
(2)没有返回值,也不能写void。
(3)方法名和类名必须一样。
(4)形参列表和成员方法一样的规则。
(5)构造器的调用,由系统完成。即在创建对象时,系统会自动地调用该类的构造器完成对象的属性初始化。
4.快速入门
现在我们就用构造方法来完成刚才提出的问题:在创建人类的对象时,就直接指定这个对象的年龄和姓名。
public class Constructor01 {
public static void main(String[] args) {
Person p = new Person("xiaolin", 25);
System.out.println("对象的属性信息:");
System.out.println("姓名=" + p.name);
System.out.println("年龄=" + p.age);
}
}
class Person {
//成员变量(属性)
String name;
int age;
public Person(String pName, int pAge) {
name = pName;
age = pAge;
}
}
5.注意事项和使用细节
(1)一个类可以定义多个不同的构造器,即构造器重载。
(2)构造器名和类名要相同。
(3)构造器没有返回值。
(4)构造器是完成对象的初始化,并不是构建对象。
(5)在创建对象时,系统自动地调用该类的构造器。
(6)如果程序员没有定义构造方法,系统会自动给类生成一个默认无参构造器(也叫默认构造器),比如 Dog(){},使用javap指令反编译看看。
(7)一旦定义了自己的构造器,默认构造器就覆盖了,就不能再使用默认无参构造器,除非显式的定义一下。
public class ConstructorDetail {
public static void main(String[] args) {
Person p1 = new Person("xiaolin", 25);//调用第1个构造方法
Person p2 = new Person("milan");//调用第2个构造方法
Dog dog1 = new Dog();//调用默认无参构造器,若定义了自己的构造器,默认无参构造器就无法再使用,除非显式地定义一下
Dog dog2 = new Dog(10);//调用自己定义的构造器
}
}
class Person {
//成员变量(属性)
String name;
int age;//默认0
//第1个构造方法
public Person(String pName, int pAge) {
name = pName;
age = pAge;
}
//第2个构造方法:只指定姓名,不指定年龄
public Person(String pName) {
name = pName;
}
}
class Dog {
/*
没有定义构造方法,系统会自动给类生成一个默认无参构造器(也叫默认构造器)
Dog() {
}
*/
public Dog(int pWeight) {
}
Dog() {//显式地定义一下默认无参构造器,才能再使用它
}
}
6.课堂练习
在定义的Person类中添加两个构造器:
第一个无参构造器:利用构造器设置所有人的age属性初始值都为18
第二个带pName和pAge两个参数的构造器:使得每次创建Person对象的同时初始化对象的age属性值和name属性值。
分别使用不同的构造器,创建对象。
public class ConstructorExercise {
public static void main(String[] args) {
Person p1 = new Person();//调用无参构造器
System.out.println("p1的姓名=" + p1.name + "\np1的年龄=" + p1.age);//null 18
Person p2 = new Person("小林", 25);
System.out.println("p2的姓名=" + p2.name + "\np2的年龄=" + p2.age);//小林 25
}
}
class Person {
String name;//默认为null
int age;//默认为0
//第1个无参构造器:利用构造器设置所有人的age属性初始值都为18
Person() {
age = 18;
}
//第2个带pName和pAge两个参数的构造器
public Person(String pName, int pAge) {
name = pName;
age = pAge;
}
}
对象创建的流程分析
1.在方法区加载Person类信息(Person.class),只会加载一次。
2.在堆中分配对象空间(得到一个地址)。
3.完成对象初始化。
(1)默认初始化 age = 0, name = null
(2)显式初始化 age = 90, name = null
(3)构造器的初始化 age = 20, name = 小倩
4.将对象在堆中的地址,返回给p(p是对象名,也可以理解成是对象的引用)。
八、this
1.引出this
public class This01 {
public static void main(String[] args) {
Dog dog1 = new Dog("大壮", 3);
System.out.println("dog1的hashCode=" + dog1.hashCode());//将dog1的hashCode与dog1对象的this的hashCode比较,查看是否一致
dog1.info();
//构造器形参为dName,dAge,输出 大壮 3
//构造器形参为name,age,输出 null 0
//加入this后,输出 大壮 3
System.out.println("============================");
Dog dog2 = new Dog("大黄", 2);
System.out.println("dog2的hashCode=" + dog1.hashCode());//将dog2的hashCode与dog2对象的this的hashCode比较,查看是否一致
dog1.info();
}
}
//类
class Dog {
//成员变量(属性)
String name;
int age;
//构造器
// public Dog(String dName, int dAge) {
// name = dName;
// age = dAge;
// }
/*
构造器的输出参数名不是非常的好,如果能够将dName改成name就好了
但是出现一个问题:根据变量的作用域就近原则
构造器中的name为局部变量,而不是属性
构造器中的age为局部变量,而不是属性
----->引出this关键字来解决
*/
// public Dog(String name, int age) {
// name = name;
// age = 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());
}
//成员方法
public void info(){
System.out.println("info方法 this的hashCode" + this.hashCode());
System.out.println(name + "\t" + age + "\t");//输出的是属性的值
}
}
2.this关键字
java虚拟机会给每个对象分配this,代表当前对象。
可以将this理解为对象的一个隐藏属性,指向对象本身。
验证对象名(dog1、dog2)指向的地址和this指向的地址是否一致?
java中无法直接获取地址,可以通过hashCode间接验证。
小结:简单地说,哪个对象调用,this就代表哪个对象。
3.注意事项和使用细节
(1)this关键字可以用来访问本类的属性(this.name)、方法和构造器。
(2)this用于区分当前类的属性和局部变量。
(3)访问成员方法的语法:this.方法名(参数列表);
(4)访问构造器的语法:this(参数列表);
注意只能在构造器中使用(只能在构造器中访问另一个构造器,必须放在第一条语句)
(5)this不能在类定义的外部使用,只能在类定义的方法中使用。
public class ThisDetail {
public static void main(String[] args) {
T t = new T();
// t.f2();
t.f3();
}
}
class T {
//成员变量
String name = "jack";
int num = 10;
//this关键字可以用来访问本类的构造器
//访问构造器的语法:this(参数列表);
//注意只能在构造器中使用(只能在构造器中访问另一个构造器,必须放在第一条语句)
public T() {//构造器
//这里去访问T(String name, int age) 构造器
this("xiaolin", 25);
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();
}
//this关键字可以用来访问本类的属性
public void f3() {//成员方法
String name = "smith";
System.out.println("name=" + name +" num=" + num);//smith 10 就近原则,这里name是局部变量,num是属性
System.out.println("name=" + this.name +" num=" + this.num);//jack 10 都是属性
}
}
4.课堂练习
定义Person类,里面有name、age属性,并提供compareTo比较方法,用于判断是否和另一个人相等,提供测试类TestPerson用于测试,名字和年龄完全一样,就返回true,否则返回false.
public class TestPerson {
public static void main(String[] args) {
Person p1 = new Person("xiaolin", 25);
Person p2 = new Person("xiaolin", 2);
System.out.println(p2.compareTo(p1));//false
}
}
class Person {
String name;
int age;
public Person(String name, int age) {//构造器
this.name = name;
this.age = age;
}
public boolean compareTo(Person p) {
//字符串比较使用equals
// 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;
}
}