1.方法重载
(先来说一下方法重载这个问题,早在第一弹就应该讲的)。
方法重载简单来说就是方法名相同,而参数列表不同,上一弹的不同的构造方法其实就是方法重载。参数列表不同分为三种情况,参数数量不同、参数类型不同、参数顺序不同。
这里注意:方法重载与函数返回值类型无关,与参数名称无关,只与参数数量、类型、顺序有关。
1.1.参数数量不同
public class demo {
public static void main(String[] args) {
System.out.println(add(1,2));
System.out.println(add(1,2,3));
}
public static int add(int a, int b) {
return a + b;
}
public static int add(int a, int b, int c) {
return a + b + c;
}
}
1.2.参数类型不同
public class demo {
public static void main(String[] args) {
System.out.println(add(1, 2));
System.out.println(add(1.5,2.6));
}
public static int add(int a, int b) {
return a + b;
}
public static double add(double a,double b){
return a + b;
}
}
1.3.参数多类型顺序不同
public class demo {
public static void main(String[] args) {
System.out.println(add(12,"aaa"));
System.out.println(add("aaa",12));
}
public static String add(int a,String name) {
return "name"+a;
}
public static String add(String name ,int a) {
return "name"+a+"重载";
}
}
大多数人看这段代码都是有些疑惑的,顺序不同为什么要这么表现呢?不应该是下面这样吗?👇
public class demo {
public static void main(String[] args) {
System.out.println(add(1,2));
System.out.println(add(2,1));
}
public static int add(int a,int b){
return a * 10 + b;
}
public static int add(int b,int a){
return b * 10 + a;
}
}
注意上面这种做法是完全错误的,这样做编译都通不过,为什么编译通不过呢?下面看它显示的错误👇
意思是说这个函数已经定义了,这就说明这两个函数是完全相同的,再看一下main函数中报的错误👇
意思是说运行这个函数的时候匹配到两个函数,但是不知道运行哪一个,所以编译不通过。
其实方法重载的第三种方式用“参数顺序不同”这句话来表达是很苍白地,浏览了很多csdn博主对方法重载的解释,全都是用这句话来表达的,也几乎全都对第三个没举例子,即使举例子了,也很模糊,读者读完还是不明白。
第三种方式应该表示为“ 参数的多类型顺序不同 ”,“多类型”三个字便可以表达清楚方法重载的第三种方式。
2.this
总结一下this:
- this可以调用成员变量、成员方法和成员构造方法。
- this指向当前使用的对象,谁调用方法,this就指向谁。
- 可以在构造方法中通过this(形参)来调用其他重载的构造方法。注意:this(形参)必须放在构造方法的首行,否则编译会会报错"Call to ‘this()’ must be first statement in constructor body",翻译便知。
2.1.this调用成员变量
this为了解决什么问题呢?请看代码。
User类👇
public class User {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
注意:我在User类里重写了toString方法,会打印User{id=1001, name=‘TalentO_o’}这种格式,否则会打印com.hpe.java.User@1b6d3586这种格式。
main方法👇
public class demo {
public static void main(String[] args) {
User user = new User();
user.setId(1001);
user.setName("TalentO_o");
System.out.println(user);//User{id=0, name='null'}
}
}
为什么打印的值不对呢?其实可以看出来是User类里的setter方法出了问题,局部变量名和成员变量名相同,逻辑出现错误,所以打印默认值(int类型打印0,String类型打印null)。
为了解决这个问题,就需要用到this,并且我们打印一下this,把setter方法改为如下👇
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
System.out.println(this);//User{id=1001, name='TalentO_o'}
}
查看一下main方法中的打印语句👇
public class demo {
public static void main(String[] args) {
User user = new User();
user.setId(1001);
user.setName("TalentO_o");
System.out.println(user);//User{id=1001, name='TalentO_o'}
}
}
可以得出setter里的this和main方法中的user:this == user;
通俗来说,谁调用了方法,this就指向谁。我们看在main方法中我们先创建了一个对象user,然后用user调用setId(1001);setName(“TalentO_o”);那么setter方法里的this就指向了这个对象user,然后再方法里的this.id和this.name就是指的这个对象的成员变量。
然后我们就解决了局部变量名和成员变量名相同的问题。很OK!
上面的代码已经写了了this调用成员变量的例子。
2.2.this调用成员方法
下面再说一下this调用方法,在User类里增加两个方法👇
public void info(){
System.out.println("id : " + this.id + ",name : " + this.name);
}
public void show(){
//this.info();
info();
}
main方法👇
public class demo {
public static void main(String[] args) {
User user = new User();
user.setId(1001);
user.setName("TalentO_o");
user.show();
}
}
这里只说一点,show()方法里的this.info();把this去掉,也是可以的,这里可以理解为系统默认给加上this。
2.3.this调用构造方法
下面的例子只是为了说明this在构造方法中的用法,在实际开发中谁闲的蛋疼这么用…
public class Person {
String name;
int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void sayHello(String name){
System.out.println(this.name + "对" + name + "说:Hello");
this.show();
}
public void show(){
System.out.println("I show.");
}
public Person() {
this("张三");
System.out.println("无参构造方法执行了");
}
public Person(String name) {
this(18);
this.name = name;
System.out.println("Person带name参数的构造方法执行了");
}
public Person(int age) {
this("李四",20);
this.age = age;
System.out.println("Person带age参数的构造方法执行了");
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
main方法👇
public class TestPerson {
public static void main(String[] args) {
Person p = new Person();
}
}
执行方法就要进栈,栈的结构是先进后出👇
这里主要是栈的问题,学过数据结构之后很容易就能明白,这里强调一点,根据什么确定执行哪个函数呢?是根据this(形参)里形参的类型或者顺序或者数量,也就是方法重载的问题,在上边已经讲过。
3.继承
面向对象的第二大特征------继承
3.1.什么是继承
一句话,允许子类拥有父类的属性和行为。
3.2.优缺点
优点:提高了代码的复用性
缺点:类与类之间的耦合度变高了
3.3.语法
class 子类 extends 父类{
}
3.4.继承的特点
- 子类继承父类后,父类声明的属性和行为,子类都可以获取。
- 子类除了通过继承父类的属性和方法,也可以定义自己独有的属性和方法,所以说子类的功能永远比父类多。
- java是单继承的,一个子类只能继承一个父类,一个父类可以拥有多个子类。一个子类可以再派生一个子类,类似于爷爷–爸爸–儿子的关系,儿子可以拥有爸爸和爷爷的属性和方法。
- 任何类都最终继承Object类。
3.5.方法重写
如果父类的方法不能满足子类的需求,子类可以重写父类的方法。
- 子类重写父类的方法,要求方法名、形参列表、返回值类型必须与父亲一致。
- 子类重写的方法的访问修饰符权限不能低于父类(大于等于)。
- 子类方法和父类方法必须同为static和非static。
3.6.方法重写(overload)和方法重载(overwrite)的区别
我认为这是很无聊的问题,这两个概念除了名字差不多以外,没有什么其他相似的地方,说这两个概念的区别完全是重新介绍这两个概念。
- 方法重载要求方法名相同、形参列表(参数个数、参数类型、参数顺序)必须不同;方法重写要求方法名、形参列表、返回值类型必须与父类一致,访问修饰符权限不能小于父类。
- 方法重载发生在一个类里;方法重写发生在子类和父类之间。
3.7.代码实例👇
父类Person
public class Person {
private String name;
private String sex;
private int age;
private int id = 370829;//身份证号
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
//构造方法
public Person() {
System.out.println("Person无参构造方法执行了");
}
public Person(int age) {
this.age = age;
}
public Person(String name) {
this.name = name;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name, String sex, int age, int id) {
this.name = name;
this.sex = sex;
this.age = age;
this.id = id;
}
//成员方法
public void eat() {
System.out.println("吃饭");
}
public void sleep() {
System.out.println("睡觉");
}
public void show() {
System.out.println("name:" + this.name
+ ",age" + this.age
+ ",sex" + this.sex
+ ",id" + this.id);
}
}
子类Student
public class Student extends Person {
//子类有自身的属性school和id
private String school;
private int id = 1001;//学号
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
//构造方法
public Student() {
/**
* 调用Student方法,会首先调用父类的无参构造方法,相当于在Student()方法体首行加了super();
* 调用完父类的无参构造方法后,回来接着执行Student()。
*/
//super();
System.out.println("Student无参构造方法执行了");
}
public Student(String school) {
//根据实参的类型和顺序,调用父类的构造方法
//super("张三");
super(20);
this.school = school;
}
//学生类独有的方法
public void game() {
System.out.println("学生在玩游戏");
}
//重写吃饭的方法
public void eat() {
System.out.println("学生正在长身体,应该多吃一些营养的东西");
}
//打印学生信息
public void info() {
System.out.println(id);//打印自身的id
System.out.println(super.getId());//获取父类的id
this.eat();//调用自身的eat方法
super.eat();//调用父类eat方法
}
}
3.8.super
super和this很相似,只不过super用来调用父类的属性、方法和构造方法
- 当子类和父类的属性名重名时,默认会使用子类的属性,可以通过’super.属性名’调用父类属性。
- 子类重写父类的方法后,如果想调用重写之前的方法,可以用’super.方法名’来调用重写前的方法。
- super修饰构造方法,通过在子类方法中使用’super(形参)'来显示的调用父类的构造方法。
- super(形参)必须放在构造方法的首行。
- 在构造方法内部,this(形参)和super(形参)都必须放在首行,所以super和this不能同时用,只能用一个。
- 在构造方法中,如果不显示的使用super(形参)的方式调用父类对应的构造方法,默认会调用父类的构造方法,只是被隐藏了。
4.访问修饰符
- public:公共的,在当前项目下的任何包都可以访问。
- protected:受保护的,在同一个包下或者在子类中可以访问到。
- default:缺省的,在同一个包下可以访问到。
- private:私有的,只能在本类中访问到。
Order类👇
package com.hpe.java;
public class Order {
private String oName;//订单名
int oId;//订单id
protected int oNum;//数量
public String oData;//时间
}
同一个包下的TestOrder类
public class TestOrder {
public static void main(String[] args) {
Order o = new Order();
//o.oName = "aaa";//oName是private修饰的,只能在Order中使用,在另一个类中使用会报错。
o.oId = 1001;
o.oNum = 10;
o.oData = "2021-02-02";
}
}
不用包下的TestOrder1类
package com.hpe.ex;
import com.hpe.java.Order;
public class TestOrder1 {
public static void main(String[] args) {
Order o = new Order();
//o.oId = 111;//oId是缺省修饰的,只能在同一个包下使用。
//o.oNum = 10;//oNum是protected修饰的,只能在同一个包下或者子类中使用。
o.oData = "2020-02-02";//oData是public修饰的,在当前项目的任何包里都可以使用。
}
}
//一个java文件里面可以定义多个类,但只能有一个是public
class Order1 extends Order{
public void method5(){
//oId = 111;//oId是缺省修饰的,只能在同一个包下使用。
oNum = 100;//oNum是protected修饰的,只能在同一个包下或者子类中使用。
}
}