目录
多态
同类型的对象,表现出的不同形态
父类对象 对象名称 = 子类对象;
学生形态
Student s = new Student();
人的形态
Person p = new Student();
多态的前提
*有继承关系 *有父类引用指向子类对象 *有方法重写
F f = new Z();
多态的好处:使用父类型作为参数,可以接收所有子类对象,体现多态的扩展性与便利
多态调用成员的特点
*变量调用:编译看左边,运行也看左边
*方法调用:编译看左边,运行看右边
多态的优势
*在多态形势下,右边对象可以实现解耦合,便于扩展和维护
Person p = new Student();
p.work(); //业务逻辑发生改变时,后续代码无需修改
*定义方法的时候,使用父类型作为参数,可以接收所有子类对象,体现多态的扩展性与便利
*方法中,使用父类型作为参数,可以接收所有子类对象
多态的弊端
*不能使用子类的特有功能
引用数据类型的类型转换,有几种方式?
自动类型转换 Person p = new Student();
强制类型转换 Studnet s = (Student)p;
强制类型转换能解决什么问题
*可以转换成真正的子类类型,从而调用子类独有功能
*转换类型与真实对象类型不一致会报错
*转换的时候用instanceof关键字进行判断
//新特性,先判断a是否为Dog类型,如果是,则强转成Dog类型,转换之后变量名为id,如果不是,则不强转,结果直接是false
if(a instanceof Dog d)
{
d.lookHome();
}
else if(a intanceof Cat c)
{
c.catchMouse();
}
else
{
System.out.println("没有这个类型,无法转换");
}
包
包就是文件夹,用来管理各种不同功能的Java类,方便后期代码维护
*包名的规则:公司域名反写 + 包的作用,需要全部英文小写,见名知意。例如:com.itheima.domain
package com.itheima.domain;
public class Student
{
私有化成员变量
构造方法
成员方法
}
全类名 全限定名(包名 + 类名)
com.itheima.domain.Student
使用其它类的规则
import com.itheima.domain.Student;
public class Test{
public static void main(String[] args){
Student s = new Student();
}
}
*使用同一个包中的类时,不需要导包
*使用java.lang包中的类时,不需要导包
*其他情况都需要导包
*如果同时使用两个包中的同名类,需要用全类名
final
最终的 ->不可被改变
方法 表明该方法是最终方法,不能被重写
类 表明该类是最终类,不能被继承
变量 变成常量,只能被赋值一次
常量:
实际开发中,常量一般作为系统的配置信息,方便维护,提高可读性
命名规范:*单个单词:全部大写 *多个单词:全部大写,单词之间用下划线隔开
细节:
final修饰的变量是基本类型:那么变量储存的数据值不能发生改变
final修饰的变量是引用类型:那么变量储存的地址值不能发生改变,对象内部的可以改变
权限修饰符
*权限修饰符:是用来控制一个成员能够被访问的范围的
*可以修饰成员变量,方法,构造方法,内部类
权限修饰符的分类
private | 私房钱,只能自己的类用 |
默认friendly | 只能本包中能用 |
protected | 受保护的,不同包下的无关类不能用,不同包下的子类可以用 |
public | 公共的,所有的都可以用 |
权限修饰符的使用规则
*成员变量私有
*方法公开
特例:如果方法中的代码是抽取其他方法中共性代码,这个方法一般也私有
代码块
*局部代码块(淘汰)
提前结束变量的生命周期
*构造代码块(不够灵活)
1.写在成员位置的代码块
2.作用:可以把多个构造方法中重复的代码抽取出来
3.执行时机:我们在创建本类对象的时候会先执行构造代码块再执行构造方法
*静态代码块
格式:static{}
特点:需要通过static关键字修饰,随着类的加载而加载,而且自动触发,只执行一次
使用场景:在类加载的时候,做一些数据初始化的时候使用
抽象类
*抽象方法:将共性的行为(方法)抽取到父类之后。由于每一个子类执行的内容是不一样的,所
以在父类中不能确定具体的方法体。该方法就可以定义为抽象方法
*抽象类:如果一个类中存在抽象方法,那么该类就必须声明为抽象类
抽象方法和抽象类的定义格式
*抽象方法的定义格式:
public abstract 返回值类型 方法名(参数列表);
*抽象类的定义格式:
public abstract class 类名{}
public class Person{
public abstract void work();
}
抽象类和抽象方法的注意事项
*抽象类不能实例化
*抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
*可以有构造方法
*抽象类的子类
要么重写抽象类中的所有抽象方法
要么是抽象类
接口
接口就是一个规则,是对行为的抽象
接口的定义和使用
*接口用关键字interface来定义
public interface 接口名{}
*接口不能实例化
*接口和类之间是实现关系,通过implements关键字表示
public class 类名 implements 接口名{}
*接口的子类(实现类)
要么重写接口中的所有抽象方法
要么是抽象类
注意1:接口和类的实现关系,可以单实现,也可以多实现
public class 类名 implements 接口名1,接口名2{}
注意2:实现类还可以在继承一个类的同时实现多个接口
public class 类名 extends 父类 implements 接口名1,接口名2{}
接口中的成员变量
*成员变量
只能是常量
默认修饰符:public static final
*没有构造方法
*成员方法
只能是抽象方法
默认修饰符:public abstract
接口和类之间的关系
*类和类的关系
继承关系,只能单继承,不能多继承,但是可以多层继承
*类和接口的关系
实现关系,可以单继承,也可以多实现,还可以在继承一个类的同时实现多个接口
*接口和接口的关系
继承关系,可以单继承,也可以多继承
细节:如果实现类实现了最下面的子接口,那么就需要重写所有的抽象方法
JDK8开始接口中的新增的方法
*JDK7以前:接口中只能定义抽象方法
*JDK8的新特性:接口中可以定义有方法体的方法(默认,静态)
(1)允许在接口中定义默认方法,需要使用关键字default修饰
作用:解决接口升级的问题
(2)接口中默认方法的定义格式:
*格式:public default 返回值类型 方法名(参数列表){ }
*范例:public default void show(){ }
(3)接口中默认方法的注意事项:
*默认
*JDK9的新特性:接口中可以定义私有方法
初始内部类
类的五大成员:属性、方法、构造方法、代码块、内部类
什么是内部类
在一个类的里面,定义一个类
举例:在A类的内部定义B类,B类就被称为内部类
public class Outer
{ //外部类
public class Inner
{ //内部类
}
}
public class Test
{ //外部其他类
public static void main(String[ ] args)
{
}
}
内部类表示的事物是外部类的一部分
内部类单独出现没有任何意义
内部类的访问特点:
*内部类可以直接访问外部类的成员,包括私有
*外部类要访问内部类的成员,必须创建对象
内部类的分类
*成员内部类 *静态内部类 *局部内部类 *匿名内部类
成员内部类
*写在成员位置的,属于外部类的成员
*成员内部类可以被一些修饰符所修饰,比如:private,默认,protected,public,static等
public class Car
{ //外部类
String carName;
int carAge;
int carColor;
class Engine
{ //成员内部类
String engineName;
int engineAge;
}
}
获取成员内部类对象
方法一:当成员内部类被private修饰时,在外部类中编写方法,对外提供内部类的对象。
方法二:当成员内部类被非私有修饰时,直接创建格式:外部类名.内部类名. 对象名=外部类对象.内部类对象
范例:Outer.Inner oi = new Outer().new Inner();
当外部类成员变量和内部类成员变量重名时
System.out.println(Outer.this.变量名);
可以直接获取到外部类的成员变量
public class Outer
{
private int a = 10;
class Inner
{
private int a = 20;
public void show()
{
int a = 30;
System.out.println(a); //30
System.out.println(this.a); //20
System.out.println(Outer.this.a); //10
}
}
}
静态内部类
静态内部类是一种特殊的成员内部类,静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问非静态的需要创建对象
public class Car
{ //外部类
String carName;
int carAge;
int carColor;
static class Engine
{ //静态内部类
String engineName;
int engineAge;
}
}
创建静态内部类对象的格式:外部类名.内部类名 对象名 = new 外部类名.内部类名();
Outer.Inner oi = new Outer.Inner();
调用非静态方法的格式:先创建对象,用对象调用
调用静态方法的格式:外部类名.内部类名.方法名();
public class Outer
{
int a = 10;
static int b = 20;
static class Inner
{
public void show1()
{
Outer o = new Outer();
System.out.println(o.a);
System.out.println(b);
}
public static void show2()
{ //System.out.println("静态的方法被调用了");
Outer o = new Outer();
System.out.println(o.a);
System.out.println(b);
}
}
}
//只要是静态的东西,都可以用类名点直接获取
public class Test
{
public static void mian(String[ ] args)
{
Outer.Inner oi = new Outer.Inner();
oi.show1();
Outer.Inner.show2(); //静态方法
}
}
局部内部类
1.将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量
2.外界是无法直接使用,需要在方法内部创建对象并使用
3.该类可以直接访问外部类的成员,也可以访问方法内的局部变量
匿名内部类
匿名内部类本质上就是隐藏了名字的内部类,可以写在成员位置,也可以写在局部位置
格式:
new 类名或者接口名()
{
重写方法; //继承\实现,方法重写,创建对象
};
格式细节:包含了继承或实现,方法重写,创建对象。
整体就是一个类的子类对象或者接口的实现类对象
举例:
new Inter(){
public void show()
{
}
};
小拓展:
//整体我们可以理解为Swim接口的实现类对象
//接口多态
Swim s = new Swim()
{
public void swim()
{
System.out.println("重写之后游泳方式");
}
};
//编译看左边,运行看右边的原则
s.swim();
new Swim()
{
public void swim()
{
System.out.println("重写之后游泳方式");
}
}.swim();
使用场景:当方法参数是接口或者类时,以接口为例,可以传递这个接口的实现类对象,如果实现类只要使用一次,就可以用匿名内部类简化代码
常用API
Math
*是一个帮助我们用于数学计算的工具类
*私有化构造方法,所有的方法都是静态的
Math类的常用方法
方法名 | 说明 |
---|---|
public static int abs(int a) | 获取参数绝对值 |
public static double ceil(double a) | 向上取整 |
public static double floor(double a) | 向下取整 |
public static int round(float a) | 四舍五入 |
public static int max(int a,int b) | 获取两个int之中的较大值 |
public static double pow(double a,double b) | 返回a的b次幂的值 |
public static double random() | 返回值为double的随机值,范围[0.0,1.0) |
时间原点:1970年1月1日 0:0:0,我国在东八区,有8小时时差
1秒 = 1000毫秒
abs 获取参数绝对值
System.out.println(Math.abs(88)); //88
System.out.println(Math.abs(-88)); //88
//bug:以int类型为例,取值范围: -2147483648 ~ 2147483647
//如果没有正数与负数对应,那么传递负数结果有误,-2147483648没有正数与之对应,所以ads结果产生bug
//System.out.println(Math.abs(-2147483648)); //会报错
进一法:望数轴的正方向进一
System.out.println(Math.ceil(12.34)); //13.0
System.out.println(Math.ceil(12.54)); //13.0
System.out.println(Math.ceil(-12.34)); //-12.0
System.out.println(Math.ceil(-12.54)); //-12.0
//向上取整(进一法),向正无穷大方向获取距离最近的整数
去尾法
System.out.println(Math.floor(12.34)); //12.0
System.out.println(Math.floor(12.54)); //12.0
System.out.println(Math.floor(-12.34)); //-13.0
System.out.println(Math.floor(-12.54)); //-13.0
//向下取整(去尾法),向负无穷大方向获取距离最近的整数
//四舍五入
System.out.println(Math.round(12.34)); //12.0
System.out.println(Math.round(12.54)); //13.0
System.out.println(Math.round(-12.34)); //-12.0
System.out.println(Math.round(-12.54)); //-13.0
//获取两个整数的较大值
System.out.println(Math.max(20,30)); //30
//获取两个整数的较小值
System.out.println(Math.min(20,30)); //20
//获取a的b次幂
System.out.println(Math.pow(2,3)); //8
//如果第二参数为0~1之间的小数
System.out.println(Math.pow(4,0.5)); //2
System.out.println(Math.sqrt(4)); //2 返回平方根
System.out.println(Math.cbrt(8)); //2 返回立方根
获取1~100的随机数:
System.out.println(Math.floor(Math.random() * 100) + 1);
//Math.random() [0.0 1.0)
//* 100 [0.0 100.0)
//floor 去掉了后面的小数
//+1 [1 100.0]
System
System也是一个工具类,提供了一些与系统相关的方法
方法名 | 说明 |
public static void exit(int status) | 终止当前运行的Java虚拟机 |
public static long currentTimeMillis() | 返回当前系统的时间毫秒值 |
public static void arraycopy(数组源数组,起始索引,目的地数组,起始索引,拷贝个数) | 数组拷贝 |
exit方法的形参:
0:表示当前虚拟机是正常停止
非0:表示当前虚拟机异常停止
System.exit(0);
返回当前系统的时间毫秒值的方法可以来计算代码运行时间
long start = System.currentTimeMillis();
........运行的代码
long end = System.currentTimeMillis();
//获取程序运行的总时间
System.out.println(end - start);
拷贝数组
int[] arr1 = {1,2,3,4,5,6,7,8,9,10};
int[] arr2 = new int[10];
//把arr1数组中的数据拷贝到arr2中
System.arraycopy(arr1,0,arr2,0,10);
(1)如果数组源数组和目的地数组都是基本数据类型,那么两者的类型必须保持一致,否则会报错
(2)在拷贝的时候需要考虑数组的长度,如果超出范围也会报错
(3)如果数组源和目的地数组都是引用数据类型,那么子类类型可以赋值给父类类型
(3)
Student s1 = new Student("十八号",18);
Student s2 = new Student("十九号",19);
Student s3 = new Student("二十号",20);
Student[] arr1 = {s1,s2,s3};
Person[] arr2 = new Person[3];
//把arr1中对象的地址值赋值给arr2中
System.arraycopy(arr1,0,arr2,0,3);
//遍历数组arr2
for(int i = 0; i < arr2.length; i++)
{
Student stu = (Student) arr2[i];
System.out.println(stu.getName() + ", " + stu.getAge());
}
Runtime
Runtime表示当前虚拟机的运行环境
方法名 | 说明 |
---|---|
public static Runtime getRuntime() | 当前系统的运行环境对象 |
public void exit(int status) | 停止虚拟机 |
public int availableProcessors() | 获得CPU的线程数 |
public long maxMemory() | JVM能从系统中获取总内存大小(单位byte) |
public long totalMemory() | JVM已经从系统中获取总内存大小(单位byte) |
public long freeMemory() | JVM剩余内存大小(单位byte) |
public Process exec(String command) | 运行cmd命令 |
//1.获取Runtime的对象
Runtime r1 = Runtime.getRuntime();
//2.exit 停止虚拟机
Runtime.getRuntime().exit(0);
//3.获取CPU的线程数
System.out.println(Runtime.getRuntime().availableProcessors());
//4.总内存大小,单位byte字节
System.out.println(Runtime.getRuntime().maxMemory() / 1024 / 1024);
//5.已经获取的总内存大小,单位byte字节
System.out.println(Runtime.getRuntime().totalMemory() / 1024 / 1024);
//6.剩余内存大小
System.out.println(Runtime.getRuntime().freeMemory() / 1024 / 1024);
//7.运行cmd命令
Runtime.getRuntime().exec("shutdown -s -t 多少秒关机");
//shutdown:关机 加上参数才能执行
//-s:默认在1分钟之后关机
//-s -t 指定时间:指定关机时间
//-a:取消关机操作
//-r:关机并重启
Object
*Object是Java中的顶级父类,所有的类都直接或间接的继承于Object类
*Object类中的方法可以被所有子类访问,所以我们要学习Object类和其他的方法
方法名 | 说明 |
public Object() | 空参构造 |
在学继承中的类里的任意构造方法有隐藏的super();为什么是默认访问无参构造而不是有参构造?
因为顶级父类中只有无参构造方法
方法名 | 说明 |
public String toString() | 返回对象的字符串表示形式 |
public boolen equals(Object obj) | 比较两个对象是否相等 |
protected Object clone(int a) | 对象克隆 |
1.toString 返回对象的字符串表示形式
Object obj = new Object();
String str1 = obj.toString();
System.out.println(str1);
//细节
//System:类名
//out:静态变量
//System.out:获取打印的对象
//println():方法
//参数:表示打印的内容
//核心逻辑:当我们打印一个对象的时候,底层会调用对象的toString方法,把对象变成字符串,然后再打印在控制台上,打印完成换行处理
toString方法的结论:
如果我们打印一个对象,想要看到属性值的话,那么就重写toString方法就可以了,再重写的方法中,把对象的属性值进行拼接
2.equals 比较两个对象是否相等
(1)如果没有重写equals方法,那么默认使用Object中的方法进行比较,比较的是地址值是否相等
(2)一般来讲地址值对于我们意义不大,所以我们会重写,重写之后比较的就是对象内部的属性值
String s = "abc";
StringBuilder sb = new StringBuilder("abc");
System.out.println(s.equals(sb)); //false
//因为equals方法是被s调用的,而s是字符串,所以equals要看String类中的
//字符串中的equals方法,先判断参数是否为字符串,如果是字符串,再比较内部属性,如果参数不是字符串,直接返回false
System.out.println(sb.equals(s)); //false
//因为equals方法是被sb调用的,而sb是StringBuilder,所以这里的equals方法要看StringBuilder中的equals方法,那么在StringBuilder当中,没有重写equals方法,使用的是Object中的,在Object当中默认是使用==号比较两个对象的地址值,而这里的s和sb记录的地址值是不一样的,所以结果返回false
Lambda表达式
假设有这样的一个需求:设计一个设计一个方法,能够实现两个数值的加法和减法运算。java中方法不能单独存在,必须定义在类或接口中,考虑到是一个通用方法,可以设计一个数值计算接口,其中定义该通用方法。代码如下:
//可计算接口
public interface Calculable{
//计算两个int数值
int calculateInt(int a,int b);
}