* 重载的概念:
在同一个类中,允许存在一个以上的同名方法,只要他们的参数个数或者参数类型不同即可。
* 重载的特点:
与返回值无关,只看参数列表,且参数列表必须不同。(参数个数或参数类型)。调用时,根据方法参数列表的不同来区别。
重载实例:
//返回两个整数的和
int add(int x, int y){return x + y;}
//返回三个整数的和
int add(int x, int y, int z){return x+y+z;}
//返回两个整数的和
double add(double x, double y){return x+y;}
与void show(int a, char b, double c){ } 构成重载的有:
a) void show(int x, char y, double z){ } //不构成重载,因为3个参数类型相同,虽然参数名不相同。
b) int show(int a, double b, char c) { } //构成重载,因为3个参数类型不同,(顺序位置不同即可)
c) void show(int a, double c, char b){ } //构成重载,因为3个参数类型不同,(顺序位置不同即可)
d) boolean show(int c, char b){ } //重载
e) void show(double c){ } //重载
f) void shows(int x ,char y, double z){ } //非重载
g) void shows() {double c} //非重载,因为方法名不一样
* 编写程序,定义三个重载方法并调用。方法名为mOL。
- 三个方法分别接受一个int参数、两个int参数、一个字符串参数。分别执行平方运算并输出结果,输出字符串信息。
- 在主类main()方法中分别用参数区别调用三个方法
public class Test { //类名的首字母要大写
public static void main(String[] args){
Test t = new Test ();
// t.mOL(2);
// t.mOL(3, 4);
// t.mOL("方法重载mOL");
}
public void mOL(int i){
System.out.println(i * i * i);
}
public void mOL(int x, int y){
System.out.println(x * y);
}
public void mOL(String s){
System.out.println(s);
}
}
定义三个重载方法max(),第一个方法求两个int值中的最大值,第二个方法求两个double值中的最大值,并分别调用三个方法。
public class Test { //类名的首字母要大写
public static void main(String[] args){
Test t = new Test ();
t.max(3,4);
t.max(5.5, 6.6);
t.max(5, 6, 7);
}
public void max(int x, int y){
System.out.println(x > y ? x : y);
}
public void max(double x, double y){
System.out.println(x > y ? x : y);
}
public void max(double m, double n, double k){
System.out.println(m > n ? (m > k ? m : k):(n > k ? m : k));
}
}
* 下面采用数组形参来定义方法
- public static void test(int a, String[] books);
* 以可变个数形参来定义方法
- public static void test(int a, String...books);
说明:
1.可变参数:方法名参数部分指定类型的参数个数是可变多个
2.声明方式:方法名(参数的类型名....参数名)
3.可变参数方法的使用与方法参数部分使用数字组是一致的
4.方法的参数部分有可变形参,需要放在形参声明的最后 ,
如 可以 public void printInfol(int i, String... args)
但不可以 public void printInfol(String... args, int i)
public class Person {
/**
* 用数组的方式来传递可变个数的参数
* 如果没有参数,就要定义一个空数组或者是null
* @param args
*/
public void printInfo(String[] args){
for(int i = 0; i < args.length; i++){
System.out.println(args[i]);
}
}
/**
*用java特有的...的方式来传递可变个数的参数,这种参数在使用时与数组的使用方式相同
*如果没有参数就可以不填
*这种...代表可以传递0到多个参数
*如果一个方法有多个的形参(...这种的参数)一定要放在所有的参数最后
*可以 public void printInfol(int i, String... args)
*但不可以 public void printInfol(String... args, int i)
*/
public void printInfol(String...args){
for(int i = 0; i < args.length; i++){
System.out.println(args[i]);
}
}
}
public class Test { //类名的首字母要大写
public static void main(String[] args){
Person p = new Person();
// String[] ss = new String[]{"张三","11岁"};
// p.printInfo(ss);
String[] ss1 = new String[]{"北京市867","1314521","12345678910"};
// p.printInfo(ss1);
p.printInfol("李四","23","男");
String[] ss2 = new String[]{"北京市867","1314521","12345678910"};
p.printInfol(ss1);
p.printInfol(ss2);//也能正常调用,即形参长度可变
p.printInfol(); //...的方式没有参数可以不填
p.printInfo(null); //数组的方式,没有参数要写null
}
}
* 方法,必须有其所在类或对象调用才有意义。若方法含有参数:
* 形参:方法声明时的参数
* 实参:方法调用时实际传递给形参的参数值
* java的实参如何传入方法呢?
- java里方法的参数传递方式只有一种:值传递。即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响
public class Test { //类名的首字母要大写
/**
*先执行int a = 0; 在栈中开辟一块内存,地址就是AD8500,存的值是0
*调用swap方法,执行int i部分,在栈中开辟一块内存,地址是AD8600,
*值就是从a那里复制过来的值0,执行swap的方法体里面的代码,i=6,把i在栈中的值再改成
*6,最终i在栈中的值就是6。
*总之,基本数据类型在参数传递的过程中,就是把实参的值赋值到形参上
*/
public static void swap(int i){
i = 6;
System.out.println("swap方法中的参数i的值:" + i);
}
public static void main(String[] args){
int a = 0;
swap(a);
System.out.println("main方法中的a的值:" + a);
}
}
public class Test { //类名的首字母要大写
public static void swap(DataSwap ds1){ //形参为DataSwap ds1
ds1.a = 6;
System.out.println("在swap方法中,dsl.a的值是:" + ds1.a);
}
public static void main(String[] args){
DataSwap ds = new DataSwap();
//把new DataSwap()存到堆内存中,其在堆中的地址为BE2500,
//ds为引用对象,把ds存到栈中,地址是AD9500,值为new DataSwap()在堆中的地址BE2500
System.out.println("调用swap方法之前,ds.a的值是:" + ds.a);
swap(ds);//调用swap()方法,先给ds1引用对象保存到栈中,ds1在栈中的地址是AD9600,存的值来源于实参(ds),值为ds的栈中存的值
System.out.println("调用swap方法之后,ds.a的值是:" + ds.a);
//
}
}
* 使用private关键字对成员变量进行封装
* 使用者对内部定义的属性(对象的成员变量)的直接操作会导致数据错误、混乱或安全性问题。
* Java中通过将数据声明为私有的private,再提供公共的public方法:getXxx()和setXxx()实现以下目的:
- 隐藏一个类中不需要对外提供的实现 细节;
- 使用者只能通过实现制定好的方法来访问数据,可以方便地加入控制逻辑,限制对属性的不合理操作;
- 便于修改代码,增强代码的可维护性。
package day06;
public class Person {
// public int age;
//所以我们需要对这样不能让调用者随意使用的属性做封装和隐藏
//封装和隐藏步骤:①私有化变量。②写一个与变量相关联的函数来专门规定此变量有哪些属性限制
private int age; //①
public void printAge(){
System.out.println("年龄:" + age);
}
public void setAge(int a){ //②
if(a <= 150 && a >= 0){
age = a;
}else{
System.out.println("输入的年龄:" + a + "不合法");
}
}
}
package day06;
import day06.Person;
public class Test {
public static void main(String[] args){
Person p = new Person();
p.setAge(100); //这样的情况,程序是对的,能执行,但是不符合正常逻辑
//像这种情况,是把类的属性开放出来,让调用者随意使用,这样会有问题,
//所以我们需要对这样不能让调用者随意使用的属性做封装和隐藏
p.printAge();
}
**4.1 面向对象特征之二:继承 extends
4.2 方法的重写(override)
4.3 四种访问权限修饰符
4.4 关键字super
4.5 子类对象实例化过程
4.6 面向对象特征之三:多态
4.7 object类、包装类 **
** 为描述和处理个人信息,定义Person类:**
此时在Student类里可以直接继承一部分Person类里的属性,从而达到减少代码量的目的
package day7
public class Person(){
int age;
String name;
int sex;
public void showInfo(){
System.out.println(this.age);//this. 调用类成员变量
System.out.println(this.name);
System.out.println(this.sex);
}
}
public class Student extends Person { //Student类 继承了 Person类的属性
String school;
public void showInfo(){
System.out.println(this.age);//由于继承了Person类里的属性,所以可以直接调用类成员变量age
System.out.println(this.name);
System.out.println(this.sex);
System.out.println(this.school);
}
}
不要仅为了获取其他类中某个功能而去继承,还要考虑类之间的逻辑关系
子类不是父类的子集,而是父类的扩展
==java只支持单继承,不支持多继承 ==
package day7;
public class ManKind {
int sex;
int salary;
//下面设置set、get方法
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
public void manOrWoman(){
if (this.sex == 1)
System.out.println("man");
else if(this.sex == 0)
System.out.println("woman");
}
public void employeed(){
if(this.salary == 0)
System.out.println("no job");
else if(this.salary == 1)
System.out.println("job");
}
}
package day7;
public class Kids extends ManKind{//继承ManKind里的set、salary类成员变量
int yearsOld;
//之后生成yearsOld的get、set方法
public int getYearsOld() {
return yearsOld;
}
public void setYearsOld(int yearsOld) {
this.yearsOld = yearsOld;
}
public void printAge(){
System.out.println(this.yearsOld);
}
public static void main(String[] args){
Kids someKid = new Kids();
someKid.setSex(0);
someKid.setSalary(100);
someKid.manOrWoman();
someKid.employeed();
}
}
* 定义:在子类中可以根据需要对从父类中继承来的方法进行改造,也称方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
* 要求:
1. 重写方法必须和被重写方法具有相同的方法名称、参数列表和返回值类型。
2. 重写方法不能使用比被重写方法更严格的访问权限。
3. 重写和被重写的方法须同时为static的,或同时为非static的。
4. 子类方法抛出的异常不能大于父类被重写方法的异常。
通过Alt+/来调出需要重写的方法
1. 如果现在父类的一个方法定义成private访问权限,在子类中将此方法声明为default访问权限,name这样还叫重写吗?
答:不叫了,子类不能访问父类私有的方法。
2. 修改练习1.1中定义的类kids,在kids中重新定义employed()方法,覆盖父类ManKind中定义的employed()方法,输出“Kid should study and no job.”
package day7;
public class ManKind {
int sex;
int salary;
//下面设置set、get方法
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
public void manOrWoman(){
if (this.sex == 1)
System.out.println("man");
else if(this.sex == 0)
System.out.println("woman");
}
public void employeed(){
if(this.salary == 0)
System.out.println("no job");
else if(this.salary == 1)
System.out.println("job");
}
}
package day7;
public class Kids extends ManKind{//继承ManKind里的set、salary类成员变量
int yearsOld;
//之后生成yearsOld的get、set方法
public int getYearsOld() {
return yearsOld;
}
public void setYearsOld(int yearsOld) {
this.yearsOld = yearsOld;
}
public void printAge(){
System.out.println(this.yearsOld);
}
public static void main(String[] args){
Kids someKid = new Kids();
someKid.setSex(0);
someKid.setSalary(100);
someKid.manOrWoman();
someKid.employeed();
}
@Override
public void employeed() {
System.out.println("Kid should study and no job.");
}//重写
}
* 多态性在Java中的两种体现:
1. 方法的重载(overload)和重写(overwrite)。
- 重载:本类中的同名方法,体现相同的名称,方法实现不同的逻辑。
- 重写:子类对父类方法的覆盖,字类可以使用和父类相同的方法名,覆盖掉父类的逻辑。
例如:父类的方法想修改逻辑,但有别的代码在调用父类的方法,可以考虑用子类继承父类,然后重写父类的方法。
2. 对象的多态性--- 可以直接应用在抽象类和接口上。
- Java引用变量有两个类型:编译时类型和运行时类型。编译时类型由声明该变量的类型决定,运行时类型由世纪赋给该变量的对象决定。
如: Student stu = new Student(); 中
Student stu 为编译期类型, new Student(); 为先把new stu 放在堆内存里,再把堆内存中的地址放在栈中。这个过程就叫运行时多态。
如:List list = new ArrayList<>();中,List是接口,ArrayList是实现类。这就体现了多态性
- 若编译时类型和运行时类型不一致,就会出现多态。(对象的多态)
Person p = new Person();
Student s = new Student();
//以上是正常情况
Person e = new Student(); //父类的引用对象可以指向子类的实例
Person p = new Person();
p = new Student();
//问题? 当前这个引用对象p引用的是哪个对象实例?
* 对象的多态--在java中,子类的对象可以替代父类的对象使用
- 一个变量只能有一种确定的数据类型
- 一个引用类型变量可能指向(引用)多种不同类型的对象
* 子类可看作是特殊的父类,所以父类类型的引用可以指向子类的对象:向上转型(upcasting)
* 一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中添加的属性和方法。
Student s = new Student();
s.school = "xxx";
Person e = new Student();//向上转型
e.school = "xxxx";//报错,Person里没有school属性,因而编译错误。
属性是在编译时确定的,编译时e为Person类型,没有school成员变量,因而编译错误。
以上是对成员变量来说,而对方法来说就不是这样了!
正常的方法调用
Person = new Person():
p.getInfo();
Student s = new Student();
s.getInfo();
虚拟方法调用(多态情况下)
Person e = new Student;//编译时e为Person类型,而方法的调用实在运行时确定的,所以调用的是Student类的getInfo() 方法。
e.getInfo();//调用Student类的getInfo() 方法
编译时类型和运行时类型
编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo() 方法。—动态绑定。
* 前提:
- 需要存在继承或者实现关系
- 要有覆盖操作
* 成员方法:
- 编译时:要查看引用变量所属的类中是否有所调用的方法。
- 运行时:调用实际对象所属的类中的重写方法。
* 成员变量:
- 不具备多态性,只看引用变量所属的类。
* 若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中
* 对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量仍然不可能覆盖父类中定义的实例变量。
- Object类是所有Java类的根父类
- 如果在类的声明中未使用extends关键字指明其父类,则默认父类为Object类
public class Person{
...
}
等价于:
public class Person extends Object{
...
}
多层继承,处于最高层的父类一定是Object类
例:
public void test(Object obj){ //基类
}
public static void main(String[] args){
Test t = new Test();
Person p = new Person();
Student s = new Student();
t.test(p); //由于Object是所有类的基类,所以怎么传递参数都可以。
t.test(s); //由于Object是所有类的基类,所以怎么传递参数都可以。
t.test(new Kk()); //由于Object是所有类的基类,所以怎么传递参数都可以。
}