- 一、对Java“包”的理解
- 二、封装(类与对象篇有讲,这里简单介绍)
- 三、继承
- 前情提要
- 代码展示
- 继承的规则
- 隐式继承
- 继承访问权限:protected
- super关键字
- 前情提要
- super的用法
- super修饰属性
- super修饰方法
- super修饰普通方法
- final关键字
- 四、多态
- 向上转型
- 方法重写
- 向下转型
1. Java中的包就是win *** 作系统的文件夹,一个包一个文件夹
2. 当创建多个包的时候,用“.”分割
3. 声明一个包用package关键字
4. 包主要是为了解决同名类的问题
5. 类的全名称:包名.类名
6. 举例:package www.baidu.com
图示:
二、封装(类与对象篇有讲,这里简单介绍)导入包中的某个类
在目前的类下面需要使用其它类,就用import关键字导入类
语法:import 包名.类名;
import static–可以导入包中的静态方法和静态属性(不常用,了解即可)
常用的系统包
- java.lang: JDK的基础类——System,String,Object等
- java.util: 工具包(集合类)
- java.io: I/O开发包,文件读取和写入
- java.net: 网络编程开发包——Socket
- java.sql: 数据库开发包
三、继承 前情提要定义:使用private将属性进行封装(这个属性当前类的内部可见,对外部隐藏)
作用:保护性和易用性
举例:手机
用户拿到手机就可以用,手机功能如何实现的用户不需要知道,这个功能的实现就对用户隐藏了
代码展示方法的重载
定义:在同一个类中,定义若干个方法名相同,参数列表(参数个数、参数类型)不同,与返回值无关的方法叫方法重载。权限修饰符(范围从小到大)
private:私有权限,只在当前类的内部可见,除了这个类就不可见了
default:包访问权限,在当前包,同级目录下内可见,不包含子包
protectd:继承权限,不同包有继承关系的类之间可见
public:公开权限,当前项目可见
public class Animal {
public String name;
public Animal(String name){
this.name = name;
}
public void eat(String food){
System.out.println(this.name + "会吃" + food);
}
}
class Dog{
public String name;
public Dog(String name){
this.name = name;
}
public void eat(String food){
System.out.println(this.name + "会吃" + food);
}
}
class Test{
public static void main(String[] args) {
Animal animal = new Animal("动物");
animal.eat("食物");
Dog dog = new Dog("狗狗");
dog.eat("冻干");
}
}
输出:
动物会吃食物
狗狗会吃冻干
从上面代码我们可以看到Animal和Dog类定义了重复的属性和方法,造成代码冗余,为了解决这个问题,引出了继承这个概念
当两个类之间满足**is a关系,一定存在继承关系,这是天然发生的。
举例:Cat is a Animal Dog is a Animal
当一个类继承了另一个类,另一类中的所有属性和方法子类就拥有了,等于”子承父业“
在java中,使用extends**关键字来表示继承
语法:子类 extends 父类
举例:Dog(子类) extends Animal(父类)
于是,上述代码可以优化一下
public class Animal {
public String name;
public void eat(String food){
System.out.println(this.name + "会吃" + food);
}
}
class Dog extends Animal{}
class Test{
public static void main(String[] args) {
Animal animal = new Animal();
animal.name = "动物";
animal.eat("食物");
Dog dog = new Dog();
animal1.name = "小狗";
animal1.eat("冻干");
}
}
输出:
动物会吃食物
狗狗会吃冻干
继承的规则
- 要使用继承,一定要满足类之间is a关系
- 一个子类只能使用extends继承一个父类(单继承)
class Keji extends Dog,Animal //错误,不允许多重继承- 子类会继承父类的所有属性和方法(静态和普通都会继承),只要还是看权限修饰符
- 显示继承:public修饰的属性和方法可以直接使用
- 隐式继承:private修饰的属性和方法子类也继承了父类的属性和方法,但不能直接使用
java虽然不允许多重继承,但可以多层继承,子子孙孙无穷无尽
public class Animal {
public String name;
public void eat(String food){
System.out.println(this.name + "会吃" + food);
}
}
class Dog extends Animal{}
class Keji extends Dog{}
class Test{
public static void main(String[] args) {
Animal animal = new Animal();
animal.name = "动物";
animal.eat("食物");
Dog dog = new Dog();
animal1.name = "小狗";
animal1.eat("冻干");
Keji keji = new Keji();
animal2.name = "柯基";
animal2.eat("狗粮");
}
}
输出:
动物会吃食物
小狗会吃冻干
柯基会吃狗粮
图示:
隐式继承子类其实已经继承了父类的属性和方法,但pivate修饰的是只能在当前类看到,所以子类不能直接用
话不多说,上代码
public class Animal {
public String name;
private int age;
public void eat(String food){
System.out.println(this.name + "会吃" + food);
}
}
class Dog extends Animal{}
class Keji extends Dog{}
class Test{
public static void main(String[] args) {
Dog dog = new Dog();
dog.name = "狗狗";
dog.age = 12;//error
dog.eat("冻干");
}
}
报错:
java: age 在 animal.Animal 中是 private 访问控制
可以用父类提供的get和set方法去调用这些属性,这体现了java的保护性
public class Animal {
public String name;
private int age;
public int getAge(){
return age;
}
public void setAge(int age){
this.age = age;
}
public void eat(String food){
System.out.println(this.name + "会吃" + food);
}
}
class Dog extends Animal{}
class Test{
public static void main(String[] args) {
Dog dog = new Dog();
dog.name = "狗狗";
dog.setAge(10);
dog.getAge();
dog.eat("冻干");
}
}
输出:
10
狗狗会吃冻干
继承访问权限:protected
- 在不同包下的具有继承关系的类的内部可见,出了子类的内部就不可见了
- 在同一个包中没有继承关系的类之间也是可见的,因为protect权限>default权限,所以default有的protected都有
代码一
- 在animal包下面创建Animal类,test类
package animal;
import person.Person;
public class Animal {
protected String name = "测试人";
}
class Test{
public static void main(String[] args) {
Person person = new Person();
person.fun();
}
}
- 在person包下面创建Person类,fun()方法
package person;
import animal.Animal;
public class Person extends Animal {
public void fun(){
System.out.println("人的名字:" + name);
}
}
- 输出
人的名字:测试人
代码二
1.在person包中创建test类
class Test{
public static void main(String[] args) {
Animal animal = new Animal();
System.out.println(animal.name);//error
}
}
3.报错
java: name 在 animal.Animal 中是 protected 访问控制
super关键字 前情提要解析:代码一:Person和Animal是有继承关系的,所以可以使用protected修饰的name属性,不同包下有继承关系的子类内部是可以用的
代码二:Test类和Animal类是没有继承关系的,且不在同一个包下,所以Aniaml的name属性是不可用的
super的用法this关键字:表示当前对象的引用
- 修饰属性:表示当前对象的同名属性
- 修饰方法:表示当前对象的同名方法
- 表示当前对象的引用
- 修饰属性:表示直接从父类找同名属性
- 修饰方法:表示直接从父类找同名方法
- 要产生一个子类对象,默认首先产生父类对象
- 一个类只要有父类,那么在它实例化的时候,一定是从顶级的父类开始创建
- 当你用子类的无参构造函数创建子类对象时,会去先递归调用父类的无参构造方法
代码
public class Animal {
public Animal(){
System.out.println("1.父类的无参构造");
}
}
class Person extends Animal{
public Person(){
System.out.println("2.子类的无参构造");
}
}
class Test{
public static void main(String[] args) {
//创建一个子类对象
Person person = new Person();
}
}
输出:
1.父类的无参构造
2.子类的无参构造
简简单单一道笔试题
public class B {
public B(){
System.out.println("1.B的构造方法-----------");
}
{
System.out.println("2.B的构造块-------------");
}
static{
System.out.println("3.B的静态方法块----------");
}
}
public class D extends B{
public D(){
System.out.println("4.D的构造方法-----------");
}
{
System.out.println("5.D的构造块-------------");
}
static{
System.out.println("6.D的静态代码块----------");
}
public static void main(String[] args) {
System.out.println("7.main开始-------------");
new D();
new D();
System.out.println("8.main结束-------------");
}
}
输出结果
3.B的静态方法块----------
6.D的静态代码块----------
7.main开始-------------
2.B的构造块-------------
1.B的构造方法-----------
5.D的构造块-------------
4.D的构造方法-----------
2.B的构造块-------------
1.B的构造方法-----------
5.D的构造块-------------
4.D的构造方法-----------
8.main结束-------------
super修饰属性解析
- 由于main存在在D子类中,若JVM要调用main,首要要加载主类。 一加载主类,先加载主类的静态方法,D继承了B,先加载福利日的静态块才加载子类的静态块。3——6
- 静态方法加载完成,执行main方法——7
- 创建一个子类对象,首先先要创建一个顶级父类对象,调用父类对象的构造方法,这里是B类。构造块优先于构造方法执行,有几个对象调用几次构造块。——2——1
- 父类对象构造方法调用完成,开始调用子类对象的构造方法。5——4
5.又创建了一个D类对象,重复上述步骤。2——1——5——4
6.最后执行8,程序结束
3——6 7 2——1——5——4 2——1——5——4 8
先看一个代码
public class Person {
public String name = "person";
public void fun(){
System.out.println(this.name);
}
}
class China extends Person{
public String name = "china";
public void fun(){
System.out.println(this.name);
}
public static void main(String[] args) {
China china = new China();
china.fun();
}
}
输出:china
super修饰方法当我们把China类里的name注释掉后,输出结果是person
1.当有继承关系时,this关键字默认在当前类中寻找同名属性,若没有,再从父类中寻找是否有同名属性。若不写this关键字,系统会默认加上这个关键字。
在运行上述代码时,需要访问父类的属性,我们可以使用super关键字
System.out.println(super.name);
super先从直接父类寻找同名属性,若不存在,在向上寻找
若Person extends Animal,而Person类的name属性是private修饰的,这时候china无权访问Animal的属性。也就是,直接父类找到了,不在向上搜寻
不能使用super直接调用private修饰的属性
看一段代码
public class Animal {
public Animal(){
System.out.println("顶级父类的无参构造");
}
}
class Person extends Animal{
public Person(){
System.out.println("直接父类的无参构造");
}
}
class China extends Person{
public China(){
System.out.println("子类的无参构造");
}
public static void main(String[] args) {
China china = new China();
}
}
输出
顶级父类的无参构造
直接父类的无参构造
子类的无参构造
- 当产生子类对象时,默认先产生父类对象;若父类对象还有继承,继续向上产生顶级父类对象。
2.这个思维很自然,没有祖宗,何来后代?祖宗的一些东西都没准备好,后代怎么继承去用?- 要产生父类对象,就要先调用父类的构造方法。
- 当创建子类对象,系统会默认调用父类的无参构造super(),若父类中没有无参构造,子类构造方法的首行需要显示的调用父类的有参构造super(参数)
- 子类构造方法中,不能显示的调用this(),super()
public class Person{
private String name;
public Person(String name){
this.name = name;
System.out.println("直接父类的有参构造");
}
}
class China extends Person{
public China(){
//要产生父类对象,就要调用父类的构造方法
super("爸爸");
System.out.println("子类的无参构造");
}
public static void main(String[] args) {
China china = new China();
}
}
super修饰普通方法
final关键字和修饰属性一样,直接从父类中寻找同名方法,就近匹配原则
- super和this的最大区别,不能直接引用父类对象
2.想要访问父类的属性和方法,需要super.方法()
四、多态final就是个终结器,修饰属性属性值不能变;修饰类,这个类无法被继承;修饰方法,表示这个方法不能被复写
向上转型定义:一个引用可以表现出多种行为/特性,就是多态性
创建一个Dog对象
Dog dog = new Dog();
类名称 类引用 = new 类对象();
若Dog extends Animal
我们可以这样写:
Animal animal = new Dog()
父类名称 父类引用 = new 子类实例();
解析:这两个类满足is a关系,狗一定是动物,所以dog既是Dog类也是Animal类,Animal有的属性和方法Dog都有,天然语义。
向上转型发生在有继承关系的类之间,顶级父类可以引用所有的子类,层层关系,不一定是直接的子类。
我们可以假设一下,如果没有向上转型,当我要写fun(参数类型 参数名)这个方法的时候,有多少个类就要重载fun方法多少次,因为即使是有继承关系的类,他们还是不同的类型,就构成了方法重载。
当我要调用fun方法,我需要知道所有类的对象,因为每个类都有一个fun方法,就会很繁琐。
这体现了Java的易扩展性,当产生一个新的子类的时候,我依旧可以用一个共同的父类引用去指代所有子类对象
所以直接用一个顶级父类去引用所有的子类,何乐而不为呢?代码如下所示
public class Animal {}
class Bird extends Animal{}
class Duck extends Animal{}
class Dog extends Animal{}
class Test {
public static void main(String[] args) {
fun(new Animal());
fun(new Duck());
fun(new Dog());
}
// 只要是Animal及其子类,都是天然的Animal对象,都满足is a关系
// 通过Animal最顶层的父类引用,指代所有的子类对象。
public static void fun(Animal animal) {}
}
当代码变成了如下样子,会产生什么结果呢?
public class Animal {
public void eat(){
System.out.println("Animal的eat方法");
}
}
class Bird extends Animal{
public void eat(){
System.out.println("Bird的eat方法");
}
}
class Duck extends Animal{
//方法重写
public void eat(){
System.out.println("Duck的eat方法");
}
}
class Dog extends Animal{
//方法重写
public void eat(){
System.out.println("Dog的eat方法");
}
}
class Test {
public static void main(String[] args) {
Animal animal1 = new Bird();
Animal animal2 = new Duck();
Animal animal3 = new Dog();
fun(animal1);
fun(animal2);
fun(animal3);
}
public static void fun(Animal animal) {
animal.eat();
}
}
输出:
Animal的eat方法
Duck的eat方法
Dog的eat方法
方法重写同一个方法名,根据传入参数对象的不同,表现出来了不同的行为,这就是多态性。
那么这种特性是如何来的呢=>引入方法重写
定义:发生在有继承关系的类之间,子类定义了和父类除了权限不同,其他全都相同的方法,这样的一组方法叫方法重写。
规则:不用去看前半部分,看当前是通过哪个类new的对象,若该类对象重写的相关方法,则调用的一定是重写后的方法。
上述代码分析:
fun(animal1); //不看前半部分,看animal1对象new的是Dog类,所以调用的是Dog类的eat方法,其他两个对象类似。
若子类没有重写相关方法,则向上搜寻碰到第一个父类重写的方法,就调用最“接近”的相关方法。(一层层向上找,找到了就调用)
- 重写权限:当发生重写时,子类权限必须>=父类权限才可以重写(除了private,因为父类里的方法是私有的,子类继承了但没有权限调用)
- static方法不能重写,因为多态的本质就是因为调用了不同的子类对象,这些对象所属的类重写相应的方法。static与对象无关,何谈重写呢?
public class Animal {
protected void eat(){
System.out.println("Animal的eat方法");
}
}
class Dog extends Animal{
void eat(){
System.out.println("Dog的eat方法");
}
}
class Test {
public static void main(String[] args) {
fun(new Animal());
fun(new Dog());
}
public static void fun(Animal animal) {
animal.eat();
}
}
报错:
java: polymorphism.Dog中的eat()无法覆盖polymorphism.Animal中的eat()
正在尝试分配更低的访问权限; 以前为protected
Animal的eat方法是用protected修饰的,Dog的eat方法是用default修饰的,default
总结:向上转型一共可以发生在三个位置
- 方法的传参——上述代码有体现
public static void fun(Animal animal) {}
- 引用赋值时
Animal animal1 = new Bird();
- 方法的返回值——实例返回类型是Animal,返回值是bird
public static Animal test() { Bird bird = new Bird(); return bird; }
看一个代码
public class B {
public B(){
fun();
}
public void fun(){
System.out.println("B.fun");
}
}
class D extends B{
private int num = 10;
public void fun(){
System.out.println("D.fun,num = " + num);
}
public static void main(String[] args) {
D d = new D();
System.out.println(d.num);
}
}
输出
D.fun,num = 0
10
向下转型原因:创建了D的对象,自动调用D的无参构造方法。
public D(){
super(); //调用父类的无参构造
num = 10; }
因为D继承了B,所以先创建B的对象。
创建B的对象,先调用B的无参构造方法。
由于子类重写了fun方法,并且new的是子类对象,所以调用了子类重写的fun方法,输出num = 0;
接着调用D的无参构造,初始化num = 10;
主方法调用了d.fun,输出10;
程序结束
语法:类名称 引用名称 = new 类实例
引用名称.方法名称();
若在调用方法的时候,这个类并没有定义这个方法,而他的子类定义了这个方法,我们不能通过向上转型,即父类对象.方法名去调用,只能子类对象.方法名去调用
举例:Animal animal = new Dog();
animal.方法名();
总结:这个方法能不能调用,看animal,因为这个引用还是父类的引用,仅存在有继承关系的向上转型中。
如果我们想调用一个方法在父类没有创建,在子类创建的方法。可以采用向下转型。
Animal animal = new Dog();
animal.play();//此时是Animal类型,但父类没有play()方法 error
Dog dog = (Dog)animal //向下转型,强转
//语法:子类名称 子类引用 = (子类名称)父类引用
dog.play() //运行通过
//将父类引用强制转换为子类引用
代码如下:
public class Animal {
public void eat(){
System.out.println("Animal的eat方法");
}
}
class Dog extends Animal{
public void play(){
System.out.println("Dog的play方法,至此一个");
}
}
class Test {
public static void main(String[] args) {
Animal animal = new Dog(); //披着Animal外衣的Dog类对象
//animal.play(); //error
Dog dog = (Dog) animal; //强转成Dog类型
dog.play();
}
}
总结:要发生向下转型,首先父类引用就是用过用该类向上转型产生的。
Animal animal = new animal(); Dog dog = (Dog)animal //error
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)