方法的重写定义:
在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
我们进行示例如下所示:
父类:
package com.hy; public class Person { String name; int age; public Person(){ } public Person(String name,int age){ this.age=age; this.name=name; } public void eat(){ System.out.println("吃饭"); } public void walk(int distance){ System.out.println("走路,走的距离是,"+distance+"公里"); show(); eat(); } private void show(){ System.out.println("我是一个人"); } public Object info(){ return null; } public double info1(){ return 1.0; } }
子类:
package com.hy; public class Student extends Person{ String major; public Student(){ } public Student(String major){ this.major=major; } public void study(){ System.out.println("学习,专业是"+major); } public String getMajor() { return major; } public void setMajor(String major) { this.major = major; } //对父类中的eat( )进行了重写 public void eat(){ System.out.println("学生应该多吃有营养的食物"); } public void show(){ System.out.println("我是一个学生"); } public String info(){ return null; } public double info1(){ return 2.0; } }
测试类:方法的重写:
package com.hy; /** * 方法的重写 * 1.重写:子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作。 * 2.应用:重写以后,当创建子类对象以后,通过子类对象调用子父类中同名同参数的方法时, * 实际执行的是子类重写父类的方法 * 3.重写的规定:方法的声明:权限修饰符 返回值类型 方法名(形参列表)throws 异常的类型{ * //方法体 * } * 约定俗成,子类中的叫重写的方法,父类中的叫被重写的方法 * (1)子类重写的方法的方法名和形参列表与父类被重写的方法名和形参列表相同 * (2)子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符 * (从小到大:private default protected public) * 特殊情况:子类不能重写父类中声明为private权限的方法。 * (3)返回值类型: * >父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void * >父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类。(比如父类为Object,子类的String) * >父类被重写的方法的返回值类型是基本数据类型(比如,double),则子类重写的方法的返回值类型必须是相同的基本数据类型。(必须是double) * (4)子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型。 * 子类和父类中同名同参数的方法要么都声明为非static,要么都声明为static的(不是重写)。 * 面试题:区分方法的重载和重写。 * 方法的重载为:在同一个类中,允许存在一个以上的同名方法,只要他们的参数个数或者参数类型不同即可。 * “两个一不同”:同一个类,相同方法名,参数列表不同(有顺序的要求),参数个数不同,参数类型不同。 * 方法的重写是:子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作。, */ public class PersonTest { public static void main(String[] args) { Student student = new Student(); student.eat(); student.setMajor("计算机"); Person person = new Person(); person.eat(); student.study(); System.out.println("-------------"); student.walk(15); System.out.println(student.info1()); } }
运行之后如下所示:
B类是父类,A类是子类,Application是主类。
package com.rgf.oop.Demo08;
//B类是父类,重写都是方法的重写,和属性无关
public class B {
public static void test(){
System.out.println("B=>test");
}
}
package com.rgf.oop.Demo08;
//A类是子类,
public class A extends B{
public static void test(){
System.out.println("A=>test");
}
}
package com.rgf.oop.Demo08;
public class Application {
public static void main(String[] args) {
//方法的调用只和左边有关,定义的数据类型有关。
A a=new A();//A类
a.test();
//父类的引用指向了子类
B b=new A();
b.test();//B类
}
}
运行界面:
当我们在上述代码中去掉static之后,会出现箭头向上或者向下,代表重写。
我们在构造重写方法的时候,我们可以通过快捷键的使用,点击ALT+Insert,或者点击右键,选择Generate.
我们选择Override Methods,重写方法,进行选择所要重写的方法。
方法的重写:
package com.rgf.oop.Demo08;
//静态的方法和非静态的方法区别很大!
//静态方法://方法的调用只和左边,定义的数据类型有关
//静态方法在类一加载的时候就出来了
//非静态:重写
public class Application {
public static void main(String[] args) {
A a=new A();
a.test();
B b=new A();//子类重写了父类的方法,此方法只与非静态有关
b.test();
}
}
package com.rgf.oop.Demo08;
//B类是父类,重写都是方法的重写,和属性无关
public class B {
public void test(){
System.out.println("B=>test");
}
}
package com.rgf.oop.Demo08;
//A类是子类,
public class A extends B{
//Override 重写
@Override //注解:有功能的注释
public void test() {
System.out.println("A=>test");
}
}
运行界面如下:
重写的总结:
需要有继承关系,子类重写父类的方法!
1.方法名必须相同
2.参数列表必须相同
3.修饰符:范围可以扩大。可以从本来的小范围继承变成后面的大范围。但不能缩小public>protected>default>private
4.抛出的异常:范围可以被缩小但不能扩大。classNotFoundException(小)---->Eexception(大)
重写,子类的方法必须要和父类一致:方法体不同
为什么需要重写:
1.父类的功能,子类不一定需要,或者不一定满足!
面向对象的多态:
动态编译:类型:可扩展性
即同一方法可以根据发送对象的不同而采用多种不同的行为方式。
一个对象的实际类型是确定的,但可以指向对象的引用的的类型有很多(父类、有关系的类)
多态存在的条件:
有继承关系
子类重写父类方法
父类引用指向子类对象
注意:多态是方法的多态,属性没有多态性。
多态的代码示例如下:
父类:
package com.hy; public class Person { String name; int age; int id=1001; public Person(){ } public void eat(){ System.out.println("人,吃饭"); } public void walk(){ System.out.println("人,走路"); } }
子类一:
package com.hy; public class Man extends Person{ boolean isSmoking; int id=1002; public void earnMoney(){ System.out.println("男人负责挣钱养家"); } public void eat(){ System.out.println("男人多吃肉,长肌肉"); } public void walk(){ System.out.println("男人霸气的走路"); } }
子类二:
package com.hy; public class Woman extends Person{ boolean isBeauty; public void goShopping(){ System.out.println("女人喜欢购物"); } public void eat(){ System.out.println("女人少吃,为了减肥"); } public void walk(){ System.out.println("女人窈窕的走路"); } }
测试类:
package com.hy; /** * 面向对象特征三:多态性 * 1.理解多态性,可以理解为一个事物的多种形态。 * 2.何为多态性: * 对象的多态性,父类的引用指向子类的对象。(或子类的对象赋给父类的引用) * 3.多态的使用:虚拟方法调用 (子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象, * 动态调用属于子类的该方法,这样的方法调用在编译期是无法确定的。) * 有了对象的多态性以后,我们在编译期只能调用父类声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。 * 总结:编译,看左边,运行,看右边。 * 4.多态性的使用前提: * (1)要有类的继承关系 * (2)方法的重写 * 5.对象的多态性:只适用于方法,不适用于属性。(编译和运行都看左边) * */ public class PersonTest { public static void main(String[] args) { Person p1 = new Person(); p1.eat(); Man man = new Man(); man.eat(); man.age=25; man.earnMoney(); System.out.println("---------------"); //************************************* //对象的多态性,父类的引用指向子类的对象。 //声明的是一个变量,而提供的对象体现出了多种形态。 Person p2=new Man(); //多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法---虚拟方法调用 p2.eat(); p2.walk(); System.out.println(p2.id); // p2.earnMoney(); } }
示例二:
package com.hy; //多态性的使用举例一: public class AnimalTest { public static void main(String[] args) { AnimalTest animalTest = new AnimalTest(); animalTest.func(new Dog()); animalTest.func(new Cat()); } public void func(Animal animal){ //Animal animal=new Dog(); animal.eat(); animal.shout(); } } class Animal{ public void eat(){ System.out.println("动物,进食"); } public void shout(){ System.out.println("动物,叫"); } } class Dog extends Animal{ public void eat(){ System.out.println("狗吃骨头"); } public void shout(){ System.out.println("汪汪汪"); } } class Cat extends Animal{ public void eat(){ System.out.println("猫吃鱼"); } public void shout(){ System.out.println("喵喵喵"); } }
运行之后如下所示:
父类:
package com.rgf.oop.Demo09;
public class Application {
public static void main(String[] args) {
//一个对象的实际类型是确定的。
// new Student();
// new Person();
//可以指向的引用类型就不确定了,父类的引用指向子类
//Student 能调用的方法都是自己的或者继承父类的。
Student stu1 = new Student();
//Person 父类型,可以指向子类,但是不能调用子类独有的方法
Person stu2 = new Student();
Object stu3 = new Student();
//对象能执行哪些方法主要取决于对象左边的类型,和右边关系不大。
stu2.run(); //子类重写了父类的方法,执行子类的方法
stu1.run();
((Student)stu2).eat();//强制转换类型,将Person类型转换为Student类型,然后进行调用方法
}
}
父类:
package com.rgf.oop.Demo09;
public class Person {
public void run(){
System.out.println("run");
}
}
子类:
package com.rgf.oop.Demo09;
public class Student extends Person{
@Override
public void run() {
System.out.println("son");
}
public void eat(){
System.out.println("eat");
}
}
运行界面如下:
多态注意事项:
1.多态是方法的多态,属性没有多态
2.父类和子类,有联系,类型无法转换而强自转换会出现类型转换异常 ClassCastException!
3.存在条件:继承关系,方法需要重写,父类的引用指向的是子类对象!father f1=new Son();
无法被重写的方法:
(1)static,静态方法,属于类的,不属于实例
(2)final 常量
(3)private方法
intanceof关键字(类型转换 ) (引用类型),判断一个对象是什么类型
父类:
package com.rgf.oop.Demo09;
public class Person {
public void run(){
System.out.println("run");
}
}
package com.rgf.oop.Demo09;
public class Teacher extends Person{
}
package com.rgf.oop.Demo09;
public class Student extends Person{
}
package com.rgf.oop.Demo09;
public class Application {
public static void main(String[] args) {
//Object>String
//Object>Person>Student
Object object = new Student();
//System.out.println(X instanceof Y);看X和Y之间是否有父子关系,有则编译通过,没有则编译错误。
System.out.println(object instanceof Student);//true
System.out.println(object instanceof Object);//true
System.out.println(object instanceof Person);//true
System.out.println(object instanceof Teacher);//false
System.out.println(object instanceof String);//false
System.out.println("===============================");
Person person=new Student();
System.out.println(person instanceof Student);//true
System.out.println(person instanceof Object);//true
System.out.println(person instanceof Person);//true
System.out.println(person instanceof Teacher);//false
//System.out.println(person instanceof String);//编译报错
System.out.println("===============================");
Student student=new Student();
System.out.println(student instanceof Student);//true
System.out.println(student instanceof Object);//true
System.out.println(student instanceof Person);//true
// System.out.println(student instanceof Teacher);//编译错误
//System.out.println(student instanceof String);//编译错误
}
}
运行界面如下:
类型转换:
父类:
package com.rgf.oop.Demo09;
public class Person {
public void run(){
System.out.println("run");
}
}
子类:
package com.rgf.oop.Demo09;
public class Teacher extends Person{
}
package com.rgf.oop.Demo09;
public class Student extends Person{
public void go(){
System.out.println("go");
}
}
主类:
package com.rgf.oop.Demo09;
public class Application {
public static void main(String[] args) {
//类型之间的转化:基本类型转换 高低64 32 16 8,高转低需要强转,低转高则不需要。
//父 子
//高 低
Person student = new Student();
//student将这个对象转换为Student类型,我们就可以使用Student类型的方法了
Student student1 = (Student) student;
student1.go();
// ((Student)student).go();写到一起即为这个样子。
Student student2=new Student();
Person person=student2;
//person.go();子类转换为父类的时候,可能丢失自己本来的一些方法。
}
}
运行界面如下所示:
多态的总结:
1.父类引用指向子类的对象
2.把子类转换为父类,向上转型;
3.把父类转换为子类,向下转型;强制转换,
4.方便方法的调用,减少重复的代码:简洁
抽象:封装、继承、多态 。抽象类,接口(比抽象类还要抽象的抽象)
抽象:编程思想!持续的学习,茅塞顿开,多实践,多测试大脑中的想法,实践出真知。
方法的重载:
定义:在同一个类中,允许存在一个以上的同名方法,只要他们的参数个数或者参数类型不同即可。
“两个一不同”:同一个类,相同方法名
参数列表不同(有顺序的要求),参数个数不同,参数类型不同。
判断是否是重载:
1.跟方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系。
2.在通过对象调用方法时,如何确定某一个指定的方法:
(1)方法名(2)参数列表
重载的题目:
1.编写程序,定义三个重载方法并调用,方法名为mOL.
三个方法分别接收一个int参数、两个int参数、一个字符串参数。分别执行平方运算并输出结果,相乘并输出结果,输出字符串信息。
在主类的main( )方法中分别用参数区别调用三个方法。
代码如下:
package com.hy;
public class B {
public static void main(String[] args) {
B b = new B();
b.mOL(5);
b.mOL(3,2);
b.mOL("约翰。沃尔");
}
//如下的三个方法构成重载
public void mOL(int m){
int d = m * m;
System.out.println("平方运算结果为"+d);
}
public void mOL(int m,int n){
int e = m * n;
System.out.println("相乘的结果为:"+e);
}
public void mOL(String m){
System.out.println(m);
}
}
运行之后如下所示:
2.定义三个重载方法max( ),第一个方法求两个int值中的最大值,第二个方法求两个double值中的最大值,第三个方法求两个double值中的最大值,并分别调用三个方法。
代码如下所示:
package com.hy;
public class B {
public static void main(String[] args) {
B b = new B();
System.out.println(b.max(9,6));
b.max(6.5,9.8);
b.max(3.2,2.1,1.1);
}
public int max(int m,int n){
return m>n?m:n;
}
public void max(double m,double n){
if(m>n){
System.out.println("最大值为:"+m);
}else {
System.out.println("最大值为:"+n);
}
}
public void max(double m,double n,double l){
double v = (m > n) ? m : n;
double v1 = (v > l) ? v : l;
System.out.println("最大值为:"+v1);
}
}
运行之后如下所示:
可变个数形参的方法:
package com.hy;
public class B {
public static void main(String[] args) {
/*可变个数形参的方法
1.jdk5.0新增的内容
2.具体使用:
2.1可变个数形参的格式,数据类型...变量名
2.2当调用可变个数形参的方法时,传入的参数个数可以是:0个,1个,2个。。。多个。
2.3可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载
2.4可变个数形参的方法与本类中方法名相同,形参类型也相同的数组之间不构成重载。
换句话说,这二者之间不能共存。
2.5可变个数形参在方法的形参中,必须声明在末尾,即在声明参数的参数列表里面放在末尾
2.6可变个数形参在方法的形参中,最多只能声明一个可变形参。
*/
B b = new B();
b.max(12);
b.max(45,12,25);
b.max();
}
public void max(int...m){
System.out.println("最大值为:"+m);
}
public void max(int[] m){
}
}
方法参数的值传递机制
方法:必须由其所在类或对象调用才有意义。若方法含有参数:
形参:方法声明时的参数
实参:方法调用时实际传给形参的参数值
关于变量的赋值:
如果变量是基本数据类型,此时赋值的是变量所保存的数据值。
如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值。
方法的形参的传递机制:值传递
1.形参:方法定义时,声明的小括号内的参数
实参:方法调用时,实际传递给形参的值
2.值传递机制:
如果参数是基本数据类型,此时实参赋给形参的是,实参真实存储的数据值。
如果参数是引用数据类型,此时实参赋给形参的是,实参存储数据的地址值。
示例如下所示:
package com.hy;
public class C {
public static void main(String[] args) {
int m=10;
int n=20;
C c = new C();
c.swap(m,n);
System.out.println("m="+m+",n="+n);//此时输出的时候,swap方法已经出栈,
// 所以输出的仍然是main方法里面的数值。
}
public void swap(int m,int n){
int temp=m;
m=n;
n=temp;
System.out.println("m="+m+",n="+n);
}
}
运行之后如下所示:
示例二:引用数据类型
package com.hy;
public class C {
public static void main(String[] args) {
Date date = new Date(); //栈里面新建一个date,堆里面进行创建,初始化值为0。
date.m=10;//给堆里面的值进行赋值
date.n=20;
System.out.println("m="+date.m+",n="+date.n);
C c = new C();
c.swap(date);//此时的栈里面新建一个data,为方法里面的形参,然后指向堆里面的数值,进行交换
System.out.println("m="+date.m+",n="+date.n);//swap方法进行交换之后就要进行出栈,此时栈里面仍然有值指向堆里面的内容
}
public void swap(Date date){
int temp=date.m;
date.m=date.n;
date.n=temp;
System.out.println("m="+date.m+",n="+date.n);
}
}
class Date{
int m;
int n;
}
运行之后如下所示:
示例三:内存解析:
package com.hy;
public class D_ {
public static void main(String[] args) {
D_ d = new D_(); //在栈里面新建d,同时在堆里面新建一个方法体。
d.first(); //调用方法,我们查看方法如下。
}
public void first(){
int i=5; //在栈里面进行存储,i=5
Value v = new Value(); //v存储在栈里,但是在堆里面创建数据体,而且是new的Value,值初始为0,堆里存储为15。
v.i=25; //将堆里面指向v的方法体进行修改,i的值为25
second(v,i);//调用方法,实参为地址值,i为实际值,赋给下面的second相应的形参。
//调用second方法的时候,里面的v仍然指向堆里面的同一个地址
//调用完这个方法结束之后,我们继续进行出栈,同时堆里面的与此方法相关内容也进行删除,
// 而且所new的Value,通过second的方法修改为了20
System.out.println(v.i);
System.out.println(i);
}
public void second(Value v,int i){ //得到v:25,i:5
i=0;
v.i=20;
Value val = new Value(); //val存储在栈中,在堆里面新建一个方法体,i的初始化值为0,后改为15.
v=val; //此时赋值给v,即地址值进行赋值,即v指向了val的堆里面的方法体,为15。
System.out.println(v.i+" "+i);
}
}
class Value{
int i=15;
}
运行之后如下所示:
示例四:方式一:
package com.hy;
public class E_ {
public static void main(String[] args) {
int a=10;
int b=10;
method(a,b);//需要在method方法被调用之后,仅打印出a=100,b=200,请写出method方法的代码
System.out.println("a="+a);
System.out.println("b="+b);
}
public static void method(int a, int b){
a = 10*a;
b=b*20;
System.out.println(a);
System.out.println(b);
System.exit(0);
}
}
递归方法:
递归方法:一个方法体内调用它自身
方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制
递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环。
示例一:
package com.hy;
import javax.management.remote.rmi._RMIConnection_Stub;
import java.util.Arrays;
import java.util.Scanner;
/*
递归方法的使用(了解)
1.递归方法:一个方法内调用它自身
2.方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制。
递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环。
*/
public class Array1_ {
public static void main(String[] args) {
//计算1-100之间所有自然数的和
Array1_ array1 = new Array1_();
System.out.println(array1.getSum(100));
System.out.println(array1.getSum1(3));
}
public int getSum(int n){//3 2 1
if(n==1){
return 1;
}else {
return n+getSum(n-1); //3+getSum(2) getSum(2)=2+getSum(1)=2+1=3 3+getSum(2)=3+3=6
}
}
//计算1-100之间所有自然数的乘积 n!
public int getSum1(int n){//3 2 1
if(n==1){
return 1;
}else {
return n * getSum1(n-1); //3*getSum(2) getSum(2)=2*getSum(1)=2*1=2 3*getSum(2)=3*2=6
}
}
}
运行之后如下所示:
示例二:
已知有一个数列,f(0)=1,f(1)=4,f(n+2)=2*f(n+1)+f(n),其中n是大于0的整数,求f(10)的值。
package com.hy;
import javax.management.remote.rmi._RMIConnection_Stub;
import java.util.Arrays;
import java.util.Scanner;
/*
递归方法的使用(了解)
1.递归方法:一个方法内调用它自身
2.方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制。
递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环。
*/
public class Array1_ {
public static void main(String[] args) {
//已知有一个数列,f(0)=1,f(1)=4,f(n+2)=2*f(n+1)+f(n),其中n是大于0的整数,求f(10)的值。
Array1_ array1 = new Array1_();
System.out.println(array1.f(10));
}
public int f(int n){
//已知有一个数列,f(0)=1,f(1)=4,f(n+2)=2*f(n+1)+f(n),其中n是大于0的整数,求f(10)的值。
if(n==0){
return 1;
}else if(n==1){
return 4;
}else {
return 2*f(n-1)+f(n-2);
}
}
}
运行之后如下所示: