static关键字的特点
1)被静态修饰的随着类的加载而加载,优先于对象存在;
2)不能和this共存;
3)本身含义就是共享,共用;可以和多个对象共用;
4)被静态修饰的成员变量或者成员方法(静态变量/静态方法)的访问方式:
对于系统(jvm)来说静态的方法/变量都是类名直接访问;类名.变量名/类名.方法名()
也可以使用对象名来访问;
静态的使用场景适用范围
静态只能访问静态;
静态方法中只能访问静态变量;
静态方法只能调用静态方法;
非静态方法可以访问静态也可以访问非静态的东西;
生成数组工具说明书文档
/**
*ArrayTool类是针对数组操作的工具类,里面包含了数组的遍历功能,求数组最值问题,
* 获取数组的元素第一次出现索引值,冒泡排序等提供很多功能;
* @author ZhangYang
* @version V1.0
*/
public class ArrayTool {
//提供一个私有的构造方法,为了外界不能直接new
private ArrayTool(){}
//遍历数组---->此时构造方法私有化了,必须在成员方法里面加入一个关键字static
/**
* 这个方法是针对数组的遍历功能,将数组按照指定格式输出,输出格式如下:
* [元素1, 元素2, 元素3, 元素4,....元素n]
* @param arr 要传递的真实数组(要遍历的数组对象)
*/
public static void printArray(int[] arr){ //形式参数---引用类型,数组类型,需要传递这个数组的对象
System.out.print("[");
for(int x = 0 ; x <arr.length ; x ++){
//如果x角标取到最大索引值
if(x==arr.length-1){
System.out.println(arr[x]+"]");
}else{
System.out.print(arr[x]+", ");
}
}
}
/**
* 这个方法是获取数组中的最大值;
* @param arr 要传递数组对象,在这里面查询最大值
* @return 返回数组中的最大值
*/
public static int getMax(int[] arr){
//参照物
int max = arr[0] ;
for(int x =1; x <arr.length ;x++){
if(arr[x]>max){
max = arr[x] ;
}
}
return max ;
}
/**
* 这个方法是获取数组中第一次出现索引值
* @param arr 要查询的数组
* @param key 要查找在数组中的指定的元素
* @return 返回的就是数组的元素第一次出现的索引值,否则,返回-1
*/
public static int getIndex(int[] arr,int key){
//假设
int index = -1 ;
//遍历数组
for(int x = 0 ; x <arr.length ; x ++){
if(key == arr[x]){
index = x ;
break ;
}
}
return index ;
}
/**
* 这个方法是对数组进行冒泡排序
* @param arr 要将数组进行排序
*/
public static void bubleSort(int[] arr){
for(int x = 0 ; x < arr.length -1 ; x ++){
for(int y = 0; y < arr.length-1-x; y++){
if(arr[y] >arr[y+1]){
//互换
int temp = arr[y] ;
arr[y] = arr[y+1] ;
arr[y+1] = temp ;
}
}
}
}
}
代码块
使用{}括起来的内容
分类:
局部代码块,就是在方法定义中使用{}
{}作用,就是限定局部变量的生命周期;
构造代码块—在类的成员位置定义的{},它有特点:在执行构造方法之前,如果类中有构造代码块,优先执行构造代码块,作用:也可以给类的成员的数据进行初始化;
执行构造方法时,如果存在构造代码块,必须优先执行,然后才是构造方法…
静态代码块
类加载一次,静态代码块就执行一次.
格式
static{ //跟静态相关的都和类有关,随着类的加载而加载;
里面书写代码;
}
执行优先级:
静态代码块>构造代码块>构造方法,而且每次执行构造方法之前,优先执行所有的构造代码块
继承
将多个类的共性内容抽取出来,放在一个独立的类中,让这个独立的类和其他的类产生一种关系
继承:
将多个类的共性内容抽取出来,放在一个独立的类中,让这个独立的类和其他类产生一种关系
“继承”—关键字 extends
格式:
class 父类名{
共性内容:姓名,年龄…
提供公共的访问方法setXXX()/getXXX()
}
class 子类名 extends 父类名{}
继承的好处:
1)可以提高代码复用性
2)可以提高代码的维护性,后期便于维护,针对子类和父类进行维护(子父关系明确) 3)类与类产生的继承关系,是后面讲"多态"的前提条件;
在Java中有一个开发原则 "低耦合,高内聚"(以后所有的Java设计模式都需要遵循这一个原则)
耦合性:开发中是永远避免不了,可以降低(耦合:类和类的关系)
内聚性:指的是某个类完成某个功能的一种能力; 尽量不要产生继承关系来完成一个功能,一个类能完成一个类完成;
低耦合:降低耦合性,减少类和类的关系;
继承的特点:
1)在Java语言中,类和的类的关系是一种继承关系,这个继承只能支持"单继承",不支持多继承
class 父{}
class 父2{}
class 子 extends 父,父2{} 多继承:不支持
class 子 extends 父{} :正常的语法格式
2)类和类关系,虽然不支持多继承,但是层层单继承----> 多层继承
注意的问题
1.子类继承父类,对于非私有的成员,直接可以继承过来,但是如果私有成员,它可以通过公共的访问可以访问,但是直接访问的;
2.被私有修饰的东西(成员变量/成员方法),只能在当前类访问的;
在继承关系中,构造方法的访问问题(重点)
子类继承父类,子类的所有构造方法都默认访问父类的无参构造方法
为什么?子类继承父类,会用到父类的数据,需要让父类先进行初始化; 一个类初始化的—肯定需要执行构造方法的
面试题:
如果一个父类存在有参构造方法,没有无参构造方法,子类的所有构造会出现什么问题?出现了问题,怎么解决?
子类的所有全部构造方法报错,为什么? 子类继承父类,子类的所有构造方法都默认访问父类的无参构造方法
方案:
1)手动给出无参构造方法
2)假设,人家现在就不需要让你给出父类的无参构造方法;
就需要让子类的构造方法,显示的访问父类的有参构造方法
----要使用关键字 super:代表父类空间标识(代表的父类对象的地址值引用!)
super() :访问父类的无参构造方法
super(xxx) :访问父类的有参构造方法
这些super一定是在子类构造方法中的第一句话
方案3) 保证子类的所有的构造方法某一个构造方法,让父类初始化完毕即可;
先通过子类的无参构造方法里面—this(xxx):访问本类(子类)有参构造方法
-
在子类的有参构造方法的第一话:让父类初始化 super(xxx):间接访问父类的有参构造方法
-
子类继承父类,一定要先执行父类的构造方法,初始化完毕之后;然后才能执行子类的构造方法---分层初始化
面试题(看程序,写结果)
需要在控制台分别打印30,20,10
//考点:子类关系问题,子类和父类的成员变量一致,访问变量的流程;---->就近原则
/**
* this和super的区别
* this:代表的当前类对象的地址值引用
* super:代表的父类的空间标识(父类的对象的地址值引用)
*
* this.变量名 :访问本类的成员变量
* super.变量名:访问的父类的成员变量
*
* this.方法名():访问本类的成员方法
* super.方法名():访问父类的成员方法
*
* this():访问本类无参构造方法
* this(xxx):访问的是本类的有参构造方法
*
* super():访问父类的无参构造方法
* super(xxx):访问的父类的有参构造方法
*/
class Test{
public static void main(String[] args){
Zi zi = new Zi();
zi.show() ;
}
}
class Fu{
int num = 10;
}
class Zi extends Fu{
int num = 20 ;
public void show(){
int num = 30 ;
//补全代码
System.out.println(num);
System.out.println(this.num) ;//访问本类的成员变量 :this限定:本类的东西
System.out.println(super.num); //访问的父类的成员变量:super限定:父类的东西
}
}
子类继承父类,成员变量的访问问题;
情况1: 子类和父类的中成员变量名称不一致,访问比较简单,分别访问即可!
情况2:子类和父类的成员变量名称一致: 如何访问呢?
- 1)先在子类的局部位置找,有没有这个变量,有就使用;
- 2)如果没有,在子类成员位置中找,有没有这个变量,有就使用;
- 3)如果子类的成员位置也没有,然后会在父类的成员位置找,有没有这个变量,有就使用;
- 4)如果父类的成员位置都没有,报错(前提:这个父类没有它的父类了),说明整个子父类中都没有变量;
遵循"就近原则"
子类继承父类,关于成员方法的访问?
-
情况1:子类和父类的成员方法名称不一致,比较简单,分别调用即可;
-
情况2:子类和父类的成员方法一模一样:权限修饰符,返回值类型,参数列表都一样
子类将父类的方法覆盖了---->方法重写 :Override---->子类在父类的基础上,将父类的覆盖了,使用自己的功能;举例:
动物都需要吃
猫类和狗类吃的东西不一样,将Animal类的eat进行重写;
*/
//父类
class Fu{
//父类的成员方法
public void show(){
System.out.println("show Fu...");
}
//有一个方法
public void teach(){
System.out.println("会JavaEE...");
}
/* public void method(){
System.out.println("method Fu...");
}*/
}
//子类
class Zi extends Fu{
//子类的成员方法
public void function(){
System.out.println("function Zi...");
}
//直接将父类的方法覆盖了
//子类和父类出现的一模一样的方法
public void teach(){//override
super.teach(); //不仅沿用父亲的功能,
System.out.println("会Python,会前端,会管理项目...");
}
//子类的成员method
/* public String method(){ //系统认为父类的方法名也是method,必须加上返回值
return "hello" ;
}*/
}
//测试类
public class ExtendsDemo {
public static void main(String[] args) {
//创建子类对象
Zi z = new Zi() ;
z.show();//访问父类的方法
z.function();
z.teach() ;
}
}
final关键字
final关键字特点:
本身的含义:最终的,无法更改的
-
1)final可以修饰类,该类不能被继承
-
2)final可以修饰变量,此时这个变量是一个"常量",常驻内存;
自定义常量:在开发中:
定义一个int类型的
public static final 数据类型 xxx = 值;class Demo{ public static final int x = 100 ; // 自定义常量: 编译时期常量,不需要加载;jvm检查语法即可 //后期常用类:基本类型 int---> 自动提升 Integer 类 public static final Integer i = new Integer(100) ; 运行时期常量,因为jvm需要加载Integer类 } 类名访问: Demo.x Demo.i
-
3)final修饰成员方法,此时这个方法不能被子类重写,目的为了保证方法中某些数据的 安全性!
/**
* 需求:
* 父类中有一个方法,描述一件事情:"这个是绝密文件,任何不得更改"
* 子类如果出现了和父类一模一的方法,会将父类的方法覆盖掉,使用代码体现;
*
* 在有的情况里面,父类的功能不能让子类覆盖 ,Java提供了一个关键字"final"
*
* final:最终的,无法更改的,状态修饰符 ,被final修饰的成员方法,不能被重写,保证父类的方法安全性!
*/
class Fu{
// public void function(){
public final void function(){
System.out.println("这个是绝密文件,任何不得更改");
}
}
//子类继承父类
class Zi extends Fu{
/* public void function(){
System.out.println("这是一堆垃圾...");
}*/
}
//测试类
public class FinalDemo {
public static void main(String[] args) {
//创建子类对象
Zi z = new Zi() ;
z.function();
}
}
多态
什么是多态?
多态:
宏观角度(现实生活中):一个事物在不同时刻体现的不同形态
水事物:
气态,固态,液态
猫是动物
狗是动物
微观角度(内存中变化):具体对象在内存中的变化(对象在不同时刻的类型)
多态的前提条件 (重点)
-
1)必须有继承关系(类与类),没有继承关系,不谈多态!
-
2)必须存在方法重写,子类部分功能要将父类的功能进行覆盖,重写,子类使用自己的功能体现; 举例: 动物都需要吃; 猫和狗/猪/猴子 等等 具体动物,给出具体的吃的体现,所以应该将动物的吃进行重写;
-
3)必须存在父类引用指向 子类对象 :固定格式 class Fu{} class Zi extends Fu{} 父类名 对象名 = new 子类名() ; //向上转型:使用的父亲的东西 Fu fu = new Zi() ; //父类引用指向 子类对象 Animal a = new Cat() ;//Cat继承自Animal 猫是动物 举例 猫和狗都继承自动物类 动物类---eat()---->动物饿了都需要吃饭 sleep()---->动物困了就需要休息...
多态的成员访问特点(很重要)
父类名 对象名 = new 子类名() ;
-
1)成员变量:
编译看左,运行看左 ;
-
2)成员方法 :非静态
编译看左,运行看后, 因为子类重写了父类的功能! 静态方法: 即使子类出现了和父类一模一样静态方法,不算重写,因为静态方法都是自己类相关的,类成员! 编译看左,运行看左;
-
3)构造方法:多态的前提条件,有继承关系,跟继承一样,分层初始化
先执行父类的构造方法,然后再是子类的构造方法
//动物类
class Animal{
int age = 50 ;
public Animal(){
System.out.println("Animal的无参构造方法");
}
public void eat(){
System.out.println("动物饿了都需要吃饭");
}
public void sleep(){
System.out.println("动物困了就需要休息");
}
//父类的静态方法
public static void show(){
System.out.println("show Animal");
}
}
//猫类
class Cat extends Animal{
int age = 20 ;
public Cat(){
System.out.println("Cat的无参构造方法");
}
//将eat和sleep()具体体现,应该重写父类的方法
public static void show(){
System.out.println("show Cat");
}
@Override
public void eat() {
System.out.println("猫吃鱼");
}
@Override
public void sleep() {
System.out.println("猫躺着睡");
}
}
//狗类
class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃骨头...");
}
@Override
public void sleep() {
System.out.println("狗侧着睡...");
}
}
//测试类
public class DuoTaiDemo {
public static void main(String[] args) {
//多态进行测试:
//父类引用指向 子类对象 :固定格式
//父类名 对象名 =new 子类名() ;
Animal a = new Cat() ; //堆内存存储的猫 猫是动物
System.out.println(a.age) ;// 编译看左,运行看左 ;
a.eat(); //编译看左,运行看右 (前提存在重写)
a.sleep();编译看左,运行看右 (前提存在重写)
//调用静态show
//a.show() ; //编译看左,运行看左---show方法是静态,不推荐使用对象访问,类名访问
Animal.show();
Cat.show();
/* Animal a2 = new Dog() ; //狗是动物 堆内存中存储的是狗
a2.eat();
a2.sleep();*/
}
}
多态的弊端:
1)有继承关系了 2)有重写了 3)有父类引用指向子类对象 Fu f = new Zi() ;
无法调用子类的特有功能!
如何解决呢?
-
方案1:创建自己的子类对象
子类名 对象名 = new 子类名() ; 方案1不好地方:本身Fu fu = new Zi() ;已经开辟堆内存空间了, Zi zi = new Zi();又要在堆内存开辟空间,消耗内存空间比较大 ;
-
方案2: 多态的第三个前提条件:父类引用指向子类对象
能不能将 将父类的引用 强制为子类的引用呢?
可以:
向下转型
前提必须向上转型Fu fu = new Zi() ;
Zi z = (Zi)fu ; //强转的语法
这样的好处:节省内存空间
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KKPQ4UYd-1660383081650)(en-resource://database/572:1)]
class Fu{
public void show(){
System.out.println("show Fu");
}
}
//子类
class Zi extends Fu {
public void show() {
System.out.println("show Zi");
}
//子类有自己的特有功能
public void playGame(){
System.out.println("会玩lol");
}
}
//测试类
public class DuoTaiDemo2 {
public static void main(String[] args) {
//父类引用指向子类对象
Fu fu = new Zi() ;
fu.show() ;//编译看左,运行看右
// fu.playGame() ; //编译看左,运行看右
//方案1
Zi zi = new Zi() ;//创建子类具体对象
zi.playGame();
System.out.println("--------------------------------") ;
//子类名 对象名 = (子类型)父类对象; //向下转型
//方案2
Zi z = (Zi) fu;//分号后面alt+enter ---回车 自动补全
z.playGame();
}
}
多态的好处:
-
1)提高代码的复用性,由继承保证的
-
2)提高代码的扩展性,由多态保证 : 父类引用指向子类对象
如果在使用多态时出现 ClassCastException,说明什么?
ClassCastException :属于运行时期异常的一种; 代表"类转换异常"
使用多态操作向下转型的时候(使用不当),堆内存中 信息和栈内存信息没有子父关系
Animal a = new Cat() ; //堆内存是猫 猫是动物
//Cat c = (Cat)a; //还原成猫 猫是猫
Dog d = (Dog)a ;//语法没有问题,但是内存中有问题 (猫是狗,错误的)
抽象
有抽象方法(在具体的事物中才能能具体的行为)的类一定是抽象类; public abstract 返回值类型(参数类型 形式名称.,...) ;
抽象类的成员特点:
-
成员变量:可以是变量,也是自定义常量
-
成员方法:可以存在抽象方法,也可以存在非抽象方法
-
构造方法:可以存在无参/有参构造方法;分层初始化:先父类初始化,然后再是 子类初始化;
-
抽象类不能实例化:不能new对象 所以通过抽象类多态来实例化,通过具体的子类进行实例化 抽象类名 对象名 = new 具体的子类名() ;
abstract不能和哪些关键字使用
抽象类不能实例化,而且它是强制子类必须重写抽象方法;
-
abstract不能和 static使用:
static被类名访问.跟类相关,静态方法算不上方法重写; -
不能和private使用:
被private修饰的:只能在当前类访问 而abstract修饰的方法需要被子类重写 -
不能和final使用
被final修饰的成员方法,不能被重写; (有的jdk源码中会看到) abstract修饰的方法必须子类重写;
接口
接口的定义
interface 接口名{} 接口和类名的命名规范同理,遵循"大驼峰命名法"
接口的最大特点: 不能实例化
* 如何实例化?
接口通过它的子实现类进行实例化(前提,这个类实现 implements 接口)
class 子实现类名 implements 接口名{//实现 }
- 接口: 体现的是一种额外功能, 设计理念 "like a"的关系
- 接口和子实现类的关系:implments 实现
//接口
interface Mary{ //成员方法:只能是抽象方法
public abstract void mary() ; } //子实现类
class You implements Mary{ public viod mary(){ System.out.println("结婚了,很开心....") ;
}
}
class Test{ public static void main(String[] args){
//接口比抽象类还抽象:抽象类都不能直接new对象,接口也不能 实例化
//接口多态:接口名 对象名 = new 子实现类名() ;
Mary mary = new You() ;
mary.mary() ;
}
}
接口的成员特点
-
1)成员变量:只能是常量,存在默认修饰符 :public static final
-
2)成员方法:只能是抽象方法,存在默认修饰符 public abstract
-
3)构造方法:没有构造方法的---通过子实现类的构造方法来实例化
注: 接口本身意义:对外暴露这些功能,让子实现类实现 !
关于面向对象中牵扯关系问题:
Java中最基本的单元是类
-
类和类:继承关系 extends,Java语言中只支持单继承,不支持多继承,但是可以多层继承
-
类和接口: 实现关系 implements;一个类继承另一个类的同时,可以实现多个接口
-
接口和接口:继承关系:extends 不仅支持单继承,也可以多继承!
接口和抽象类的区别(面试题)
1)成员的区别
-
成员变量:
抽象类: 可以是变量,也可以是自定义常量
接口: 成员变量只能是常量: 存在默认修饰符 public static final 修饰的 ,可 以省略 -
成员方法
抽象类: 即可以是抽象方法,也可以是 非抽象方法
接口: 只能是抽象方法,存在默认修饰符:public abstract :可以省略 -
构造方法
抽象类: 既可以存在无参构造方法/有参构造方法,存在继承关系,分层初 始化 先父类初始化,再是子类初始化(子类需要用到父类中)
接口:没有构造方法的 ,意义:就是通过接口暴露给外面功能,实现这些功能 接口
2)关系区别
Java中最基本的单元是类,Java本身就面向接口编程
类和类:继承关系 extends,Java语言中只支持单继承,不支持多继承,但是可以多 层继承
类和接口: 实现关系 implements;一个类继承另一个类的同时,可以实现多个接口
接口和接口:继承关系:extends 不仅支持单继承,也可以多继承!
3)设计理念的区别
抽象类: 存在继承关系, 体现的是一种"is a"的关系 ,什么是什么的一种 A类是B类的一种,B类是A类的一种 ,这个时候要使用继承;
接口: 子实现类 实现关系,体现的一种"like a"的关系,什么像什么的一种 额外的扩展功能 ;