JavaBasic_09
方法的参数传递
方法调用时参数值的传递可以分为“值传递”和“引用传递”两种
值传递 —
a.当方法的参数为基本数据类型时
b.实参的值被复制给形参,改变形参不会影响实参的值
引用传递 —
a.当方法的参数为对象引用时,实参和形参都同时引用了同一对象
b.通过形参修改对象的状态后会影响对象的其它引用
区分引用传递和值传递
如果数据类型是一个类的名字,就是引用变量
如果数据类型是一个基本数据类型,就是值传递
继承
1.对真实世界中对象继承关系的支持,满足面向对象建模技术的需要(描述一种关系继承, 类之间的继承 父子
2.是面向对象程序设计语言中,对象多态性(Polymorphism)实现的前提
运行时多态可以在任意类中发生? 不行
编译时多态可以在任意类中发生? 行
3.代码复用的一种形式 继承->父类有的子类也有会 不劳而获
4.数据类型 继承帮我们摆脱了一点点,java中严格的类型限制->给我们代码带来很大的灵活性。
Animal cat
一个动物对象的类型是Animal
Animal a = new Aniamal() 动物是动物
一只猫也是一个动物
Animal b = new Cat()
java:强类型语言
A a = new A();
A a = new 子类();
如果要声明类B继承类A,则可按照以下的语法声明类B:
[修饰符] class B extends A { …… }
类B称为类A的子类(subclass),类A称为类B的父类或基类(superclass 超类),称类A的父类或父类的父类为类B的祖先(ancestor)
子类将拥有在其父类和祖先类中声明的所有的成员(但是仅可以访问到public或protected)
子类对象的成员初始化之前,必须先完成对父类或祖先类对象的成员的初始化。
1.为啥在初始化子类之前必须先初始化父类呢?
子类代码可能依赖于父类的实现,为了保证子类代码一定能够正常运行,java规定,在初始化子类对象必须先初始化父类。
2.子类对象有两种初始化方式:
a.隐式初始化
隐式初始化:(子类中,不通过调用父类的构造方法的方式,来初始化父类对象)
1.当父类提供默认的构造函数;
2.且子类的构造函数中没有显式调用父类的其他构造函数
3.则在执行子类的构造函数之前jvm会自动执行父类的构造函数,直到执行完Object的构造函数后才执行子类的构造函数
java语言中所有的类都直接或间接的继承自Object类
换句话说,只要不是Object类的类,都有父类(要么直接)
b.显式初始化:(在子类中通过显示调用父类的构造方法,来初始化父类对象)
显式初始化:可以在子类构造函数的第一句通过super()或super(...)
调用父类的默认构造函数或特定签名的构造函数
super:类比于this
1.表示当前子类对象中对父类对象的引用
2.可以通过super调用父类的构造方法
父类的隐藏
1.父类 和 子类 有同名的成员变量
2.在子类中访问和父类同名的成员变量,访问的是子类的同名成员变量的值
3.在父亲中访问和子类同名的成员变量,访问到的是父类的同名成员变量的值
4.好像在子类中,通过变量名,访问不到父类同名的成员变量, 这种现象我们称之为父类的域的隐藏
在子类中不仅有和父类的同名的成员变量,还可以有和父类方法签名完全一致的方法
1.在子类中如果调用同方法签名,执行到的是子类的同方法签名的方法代码
2.在父类中调用,和子类同方法签名的方法时,执行的仍然是子类的同方法签名的方法。
3.好像看起来,子类的方法将父类中的同方法签名给“覆盖”掉了
此时如果,需要访问父类同名的成员变量, 可以super父类成员变量 来访问父类成员变量
注意
==静态上下文中不能使用super关键字,同理this关键字也不能在静态上下文中使用==
Final
final的用途
1.final修饰的变量变成常量,不能修改。
2.被final修饰的类不能被继承,被final的方法不能被覆盖
作用或者好处:
1.使用final修饰类和方法可以提高安全性(减少出错的可能),因为你的类不能被别人通过继承的方式修改
2.使用final修饰类和方法可以加快某些类型检查过程
通过final修饰符,我们可以定义自定义常量
1.自定义常量,何时对其进行初始化
a.在声明自定义常量的时候,初始化该自定义常量
b.通过构造方法初始化,自定义常量的初值,但是必须保证每一个构造方法当中都必须对该自定义常量初始化。
包
包有以下用途:
(1)将相关的类和接口分组,包名通常表示包中各个类和接口的用途(开发中)
(2)包创建了不同的命名空间(Namespace),从而有效地解决了类型命名冲突问题(解决命名冲突)
1.同一个包中不能出现类名相同的类
2.在不同的包中可以存在同名的类(通过这样来解决命名冲突)
(3)包提供了对应程序内部实现机制的保护域 和 访问权限
默认访问权限:同包 不同 访问权限控制
比如如果某个类的成员被默认权限所修饰,不同包的类访问不到这个成员变量
如何指明,类是某个包下的类呢?
在Java源程序文件第一行使用package声明可以使文件中定义的类成为指定包的成员。
package声明的语法如下:
package<包名>
1.包名通常由多个名字字符串构成
2.中间用句号"."分割
3.每个名字表示的包称为其前面的名字表示的包的子包(类比文件夹)
com.cskaoyan.package
com/cskaoyan/package
通常如何命名包?
域名反转(通常是虚拟)
com.zs
引入
在类名前面加上类所属的包名,中间用句号“.”分隔 称为类的完全限定名(Full Qualified Name),简称类的限定名
类比操作系统上的文件名
C:\Users\GT72\Desktop\上课\Day08_Java_extends\ppt\java_extends
在操作系统中,通过文件的完整的绝对路径名,来唯一标识操作系统的一个文件
同理,在java中我们也需要唯一标识一个类,通过“完整路径名”的形式来唯一标识一个类完整路径名就是指,类所在的完整的包名路径。
比如说 ImportDemo 简单类型叫做ImportDemo
包名 + 类名,所以ImportDemo的所在路径就是该类所在的包com.cskaoyan.imports
**完全限定名**(Full Qualified Name)(com.cskaoyan.imports.ImportDemo) 就相当于java中的决定路径
当在类体中使用了与当前类不同包的类名时,编译器编译时因为无法找到该类的定义而失败
(1)使用不同包类的完全限定名
(2)使用import声明,为编译器提供该类的定义
import 声明一般紧跟在package声明之后,必须在类声明之前,其基本语法如下:
import<类的完全限定名>
Java语言核心包 java.lang包中的类将被隐式导入,可以直接使用其中的类
import声明提供了一种包的智能导入方式:
import <包名>.*;
只能导入方式不能递归导入
package com.cskaoyan.imports;
import com.cskaoyan.extend.*;
import com.cskaoyan.extend.test.Test;
public class ImportDemo {
public static void main(String[] args) {
// new Animal();
// 第一种解决方案
com.cskaoyan.extend.SonInitialization sonInitialization = new com.cskaoyan.extend.SonInitialization();
// 第二种解决方案 通过import关键字,引入该类的全限定名
SonInitialization sonInitialization1 = new SonInitialization();
ExtendDemo1 extendDemo1 = new ExtendDemo1();
//只能导包方式,不支持递归导入
Test test = new Test();
}
}
多态
概述:某一个事物(相同函数名),在不同时空表现出不同的形态(不同函数体)
多态的前提
1.要有继承关系,
2.要有方法覆盖
3.要有父类引用指向子类实例
方法覆盖(Override)
1.方法覆盖是指 在子类中替换 父类或祖先类 对某个方法的实现
2.方法覆盖通过子类重新定义与父类或祖先类中具有同样签名和返回类型的方法实现
3.覆盖的方法
a. 可以保持和父类的方法相同的可访问范围,也可以修改访问控制修饰符增大可访问范围
b.但是不能减小可访问范围
访问权限的访问范围,从小到大,依次是:
private -> default -> protected -> public
方法覆盖的注意事项:
1.被final修饰的方法不允许在子类中覆盖
2.其他修饰符,如synchronized,native修饰符修饰的方法可以被子类覆盖
3.父类被覆盖的方法参数列表被声明为final的参数,在子类的覆盖方法中可以不必指定为final
4.只有在子类类体中可以访问的父亲或祖先类的方法才能被覆盖(父类中private修饰的方法不能被覆盖)
5.静态方法不能被覆盖,只能被隐藏(域的隐藏)
6.构造方法不参与方法覆盖(构造方法不是属于成员)
public class PoliDemo {
public static void main(String[] args) {
Cat cat = new Cat();
// cat.shout();
Cat.testStatic();
Animal.testStatic();
Animal a = new Cat();
a.shout(1, 2);
}
}
class Animal {
public void eat() {
System.out.println("Animal eat");
}
public void sleep() {
System.out.println("Animal sleep");
}
//被final修饰的方法不允许在子类中覆盖
// protected final void shout() {
void shout(final int a, final int b) {
System.out.println("Animal shout");
}
//静态方法不能被覆盖,只能被隐藏(域的隐藏)
public static void testStatic() {
System.out.println("Animal static");
}
}
class Cat extends Animal {
//在子类中重新定义父类中的方法的实现(方法覆盖)
protected void shout(int a, int b) {
System.out.println("Cat shout");
}
public static void testStatic() {
System.out.println("Cat static");
}
}
多态的第三个前提条件:父类引用指向子类实例
父类类型 a = new 子类实例
也就是说,实际我们是通过父类来访问引用方法。
从数据类型的角度:父类引用指向子类实例,它的含义是:子类类型也可以被看成是父类类型
子类类型也可以被看成父类类型,从代码复用的角度去理解:
1.子类继承父类,父类中的普通成员方法和普通成员变量,子类中都有,并且都可以访问到
2.于是,通过父类对象,来访问父类中的成员的时候, 在父类中能访问到,在子类中也都可以访问到
3.所以,从可以访问到的内容的角度去理解,访问子类和访问父类,都能访问到父类的成员。
子类类型也可以被看成是父类类型
private
**static 一般不会使用对象名访问, 都会使用类名。**
以上所说的都是帮助大家去理解
子类引用指向父类实例
1.子类中的成员父类不一定都有
2.假设子类可以接收父类实例,这样一来我们就可以通过子类引用
3.但是因为,子类实际指向的是一个父类实例,在运行的时候,jvm,在父类实例找不到这个方法
**因为编译器,看的是引用变量的类型。**
运行时多态
1.子类中发生了方法覆盖的方法
对于同一个引用变量(父类引用),我们让同一个引用变量,指向不同的子类实例
在该引用变量访问同一个成员方法,该方法的行为不同。
public class PoliDemo3 {
public static void main(String[] args) {
//父类引用执行父类实例
Animal2 a1 = new Animal2();
System.out.println("i = " + a1.i);
a1.shout();
//父类引用指向子类实例
a1 = new Mammal2();
//System.out.println(a1.j);
System.out.println("i = " + a1.i);
a1.shout();
a1 = new Cat2();
System.out.println("i = " + a1.i);
a1.shout();
// 运行时多态
// 对于同一个引用变量(父类引用),我们让同一个引用变量,指向不同的子类实例,
// 在该引用变量上访问同一个成员方法,该方法的行为不同
}
}
class Animal2 {
public int i = 100;
public void shout() {
System.out.println("animal shout");
}
}
class Mammal2 extends Animal2 {
public int i = 200;
public int j = 10;
@Override
public void shout() {
System.out.println("mammal shout");
}
}
class Cat2 extends Mammal2 {
public int i = 300;
@Override
public void shout() {
System.out.println("cat shout");
}
public void go() {
}
}
多态中成员访问的特点
成员变量: 编译看左边,运行看左边
方法: 编译看左边,运行看右边
成员变量:所以,子类和同名的成员变量,我们说会发生隐藏
==编译看左边==:指的是,某个类型的引用变量,他能在(编译的时候)的那些成员,取决于该引用变量的声明
==运行看左边==:实际访问到的成员变量的值,访问到的是引用变量的声明类型中,所定义的成员变量的值。
方法:所以,子类中覆盖父类的方法,我们说发生方法覆盖
==编译看左边==:指的是某个类型的引用变量,他能在(编译的时候)访问到的那些成员,取决于该引用变量的声明
==运行看右边==:实际访问到的方法,是根据引用指向实例实际类型决定
- 成员变量-> 对象属性 ->对象外观 外观 声明类型 属性->声明类型中所定义的属性
- 成员方法-> 行为 实际的对象来执行行为 所以,方法的执行
运行的是实际类型(子类类型的行为)
多态的好处
提高代码的可维护性
提高代码的扩展性
经常是避免书写重复代码
多态的弊端:
不能使用子类的特有功能:通过父类引用只能访问(编译)父类所定义的方法
多态的第三个条件
父类引用指向子类实例
父类类型 a = new 子类类型();
也就是说,实际我们是通过父类引用来访问子类方法
如何解决
针对我们确实知道父类引用指向的实例,确实有某个方法的情况
法1:创建子类对象调用子类方法
法2:把父类的引用强转为子类引用
运算符instanceof用于判断对象(引用变量的声明类型) 是否是某个类的对象
对象a instanceof 类型 X(类名) 结果true:表示对象a是X这个类的对象
false:表示对象a不是X这个类的对象
通过instanceof运算符,规避把父类的引用强势转为子类引用所将要面对的风险
public class PoliBad {
public static void main(String[] args) {
Animal a = new Cat();
a.shout();
//在通过引用变量访问对象的成员的时候,
// 究竟通过该引用变量,能访问到哪些成员,是由引用变量的声明类型决定 编译看左边
//a.fake(); 通过父类引用无法,子类特有的方法
//1. 创建子类对象调用子类方法
// Dog dog = new Dog();
// dog.fake();
//2.把父类的引用强转为子类引用
if(a instanceof Dog) {
Dog d = (Dog) a;
d.fake();
} else {
System.out.println("不是dog类型");
}
}
}
class Animal {
public void shout() {
System.out.println("Animal shout");
}
}
class Cat extends Animal {
public void shout() {
System.out.println("cat shout");
}
}
class Dog extends Animal {
public void shout() {
System.out.println("dog shout");
}
public void fake() {
System.out.println("i like fake");
}
}
引用类型的兼容性规则
如果某个引用变量的值,可以赋值给另外一个引用变量,我们就说这两个引用类型兼容:
引用变量的赋值
赋值兼容型规则:
(1)对引用类型的变量进行赋值操作时,赋值号右边的表达式的类型必须与赋值号左边
a.对象引用类型相同
b.或是其子类型
(2)方法的return语句返回的结果的类型必须与方法声明中的返回类型相同或是其子类
所有的引用变量都可以被赋予null
public class ReferenceType {
public static void main(String[] args) {
ReferenceType r;
Son son = new Son();
//引用类型兼容
r = son;
ReferenceType referenceType = new ReferenceType();
r = referenceType;
}
}
class Son extends ReferenceType{
public ReferenceType testRefercnType() {
ReferenceType referenceType = new ReferenceType();
//子类类型的引用变量
Son son = new Son();
return son;
}
}
向上转型:把子变成父 父亲类型 a = new 子类类型()
父类引用指向子类实例
Father father = new Son();
1.new Son()表示一个子类对象,它的类型原本应该是Son
2.我们把一个原本是Son对象的地址赋值给了一个父类类类型的引用
向下转型:把父变成子 把父转成子(要求父亲的引用本来就指向子类对象)
Father f = new Father();
Son s = (Son)f
两步合一步就是 Son s =new Faher();
向下转型:
子类引用指向父类实例
Son son = new Father();
1. new Father()表示的是一个父类类型的对象
2. 我们把一个原本是Father类型的对象地址,赋值给了一个子类类型的引用
向下转型,存在风险
1.当子类没有定义只属于自己的成员的时候,此时使用强制转化,实现向下转型,此时,通过强制转转之后的引用(子类类型的引用),来访问子类中的成员。
Father f = new Father();
Son son = (Son)f;
son.访问子类成员,可以访问的
2.如果,子类中定义了只属于自己的成员
son.go()
实际开发中会不会向下转型呢?
会,比如,我们昨天利用运行时多态所写的,方法,在这个方法中,我们确实可以收集到所有动物的声音,但是,有可能我们可能对个别种类的动物,做一些特殊的操作(访问这些动物独有的成员),但是通过父类引用是无法访问到这些需要特殊操作的动物,所独有的成员,因此我们做向下转型
但是我们可以通过instanceof运算符,来规避向下转型的风险
JavaBasic_09的更多相关文章
随机推荐
- [LeetCode] 108. Convert Sorted Array to Binary Search Tree ☆(升序数组转换成一个平衡二叉树)
108. Convert Sorted Array to Binary Search Tree 描述 Given an array where elements are sorted in ascen ...
- 自建yum源解决Ceph搭建过程中从官网取包慢的问题
最近项目组需要ceph环境,第一次搭建ceph,各种不顺,装了卸,卸了装,一遍又一遍地按照官网的操作进行.最气人的是网速差,从官网取包太慢.一轮尝试就浪费一上午. 因此想到本地新建yum源. 首先,按 ...
- Linux安装/卸载软件教程
一.源码安装 ./configure #环境检查.生成makefile make #编译 make install #安装 这三条命令是最经典的Linux软件安装,适用于所有发行版 二.软件包管理工具 ...
- 维护一个旧程序 linq2sql,出现row not found or changed的异常
维护一个旧程序 linq2sql,出现row not found or changed的异常, 查博客园,文章都是一大抄,都不对. 想想之前都能保存的.这个异常是在加了字段之后出现的. 因为用vs.n ...
- 锚点 , angular 锚点 vue锚点
因为最近在开发angular,自己有路由 用window.location跳到默认路由,查了半天用angular方式不好解决 ,so 原生走起 START scrollIntoView是一个与页面(容 ...
- Vue--项目开发之实现tabbar功能来学习单文件组件2
上一篇文章里item.vue里的span标签内容是写死了,但是我们不希望写死 所以对于五个tab选项的标题需要从外部传入,也就说 需要在item.vue里的script里写上 export defau ...
- TCP可靠传输:校验和,重传控制,序号标识,滑动窗口、确认应答
Tcp通过校验和,重传控制,序号标识,滑动窗口.确认应答实现可靠传输 应答码:ACK TCP的滑动窗口机制 TCP这个协议是网络中使用的比较广泛,他是一个面向连接的可靠的传输协议.既然是一 ...
- laravel中的登录页面逻辑
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades ...
- 2-MAVEN 基本命令
MVN的基本命令 mvn package:打包 >生成了target目录 >编译了代码 >使用junit测试并生成报告 >生成代码的jar文件 >运行jar包: java ...
- shell脚本分析二
Shell 基本运算符Shell 和其他编程语言一样,支持多种运算符,包括: 算数运算符 关系运算符 布尔运算符 字符串运算符 文件测试运算符原生bash不支持简单的数学运算,但是可以通过其他命令来实 ...