package com.sxt;
import java.util.ArrayList;//导报
/**
* Created with Intellij IDEA
* Discripution:
* User:ALL
* Time:23:40
*/
public class hello {
/*
main方法 psvm
System.out.println(""); 字面常量.sout
ctrl+d 复制
代码块的移动 ctrl+shift+上箭头/下箭头
导包,生成变量 alt+enter
单行注释/取消 ctrl+/
多行注释/取消注释 ctrl+shift+/
代码块包围 ctrl+alt+t
构造方法,getter setter等等 alt+insert
*/
public static void main(String[] args) {
//1)String[] args数组的定义
// 字符串类型 char*p="hello" 数组中存的都是字符串
//数组是这样定义的
//int arr[10] ->int[10] ->vs调试的时候arr int[10]
for(int i=0;ijava test ljj is huaidan
// java命令 运行时的命令行参数
//2)sout
System.out.println("7");//换行
System.out.print("7");//不换行
//用得最多的是前两个
System.out.printf("%d\n",21);//如c语言
}
}
/*多行注释
* 多行注释
* 不推荐 */
/**文档注释
* 常用于方法和类上描述方法和类的作用
* 可以被javadoc工具解析
* 生成一套以网页文件形式体现的程序说明文档*/
java->javac .class->java运行
注意该字节码文件是一个类对应字节码文件
常量即程序运行期间,固定不变的量
字面常量的分类
1.字符串常量,“”
2.整形常量 直接写整数
3.浮点数常量 直接写小数
4.字符常量, ‘’
5.布尔常量 true false
6.空常量 null
左值=右值
表达式:变量和运算符构成的
数据类型占的byte:
在Java里面没有所谓无符号有符号注意的说法
public class TestDemo{
public static void main(String[] args){
System.out.println(Integer.MAX_VALUE);
System.out.println(Integer.MIN_VALUE);
int a = 10;
long a = 10L;//10是长整形的类型 不加L会报错,小写l不推荐
System.out.println(Long.MAX_VALUE);//9223372036854775807
System.out.println(Long.MIN_VALUE);//-9223372036854775808
short a = 10;
System.out.println(Short.MAX_VALUE);//32767
System.out.println(Short.MIN_VALUE);//-32768
byte b1 =18;
byte b2 =180;//报错
//当Java使用=赋值一个字面值常量,超过范围后会自动检查字面值常量,斌且识别为int类型
System.out.println(Byte.MAX_VALUE);//127
System.out.println(Byte.MIN_VALUE);//-128
float f =12.5f;//如果不加f会默认12.5是一个double类型
//遵循c中float的存储
//包装类Float 不研究最大值最小值
System.out.println(Float.MAX_VALUE);
System.out.println(Float.MIN_VALUE);
char ch = '搞';
System.out.println(ch);
//java里面用unicode表示字符
//用记事本出现中文会有编码错误javac -encoding UTF-8 xxx.java
}
}
Integer 叫包装类 包装类即基本数据类型所对应的类类型
int —>interger[int的plus版本]
long 8个字节->数值:63bit -2^63 ~ (2^63)-1
short 2个字节->数值:15bit -2^15 ~ (2^15)-1
byte 1个字节 ->数值—:7bit -2^7 ~ (2^7)-1
float 4个字节
double 8个字节 双精度
char 2个字节 ->对应地包装类Character
boolean 可能8bit ->Boolean
布尔变量boolean
JVM中没有指定布尔元素大小
但是说boolean数组在JVM地实现中,Java编程语言中地布尔数组被编码成JVM字节数组,每个布尔元素使用8位
没有谱所谓地非0为真 0为假
int a = 1;if(a){;}//报错
if(a!=0){;}//正确
显示隐
pubic class TestDemo{
public static void main(String[] args){
int a = 10;
long b = 20;
int c = a + b;//编译错误 a + b==>int + long --> long + long 赋值给int会丢失数据
int c = (int)(a+b);//强制类型转换
long d = a + b;//编译成功 int+long --->long + long赋值给long
}
}
由于cpu是按照4个字节为单位从内存中读取数据,byte short这种低于四个字节地类型 会提升成int再计算
+是拼接地意思 只要和字符串拼接就直接是字符串 不会进行运算
public class TestDemo{
public static void main(String[] args)
{
String st1 = "男神";
String st2 = "爱我";
System.out.println(st1+st2);
int a = 10;
int b = 20;
System.out.println("a="+a+"b="+b);
System.out.println("a+b=" +a+b);//都是字符串拼接
System.out.println("a+b="+(a+b));
System.out.println(a+b+"->a+b");//开始是整数后面字符串拼接
/*a=10b=20
a+b=1020
a+b=30
30->a+b*/
}
}
public class TestDemo{
public static void main(String[] args) {
//int转string
int num=1;
String str1=num+"";//法1
System.out.println(str1);//1
String str2=String.valueOf(num);//法2
System.out.println(str2);//1
//String转int
String str = "100";
int sum=Integer.parseInt(str);
System.out.println(sum);//100
}
}
注意:
都是二元运算符,使用时必须要有左右两个操作数
int / int 结果还是int类型,而且会向下取整
int a = 10;
int b = 20;
a + b;
a < b;
int a = 20;
int b = 10;
System.out.println(a + b); // 30
System.out.println(a - b); // 10
System.out.println(a * b); // 200
System.out.println(a / b); // 2
System.out.println(a % b); // 0 --->模运算相当于数学中除法的余数
System.out.println(11.5 % 2.0);
// 运行结果1.5
System.out.println(1+0.2); // +的左侧是int,右侧是double,在加之前int被提升为double
// 故:输出1.2
方法签名:经过编译器修改之后最终的方法名字
方法全路径名+参数列表+返回值类型
关于调用栈
方法调用的时候,会有一个栈这样的内存空间描述当前的调用关系,称为调用栈
每一次方法的调用就称为一个栈帧,每一个栈帧中包括了这次调用的参数是哪些,返回哪里等消息
可以借用idea看到
基本数据创建的变量叫做基本变量,其变量空间存储的是所对应的值,
引用数据创建的变量叫做对象的引用,存贮的是对象所在空间地址
public static void func() {
int a = 10;
int b = 20;
int[] arr = new int[]{1,2,3};
}
在上述代码中,a、b、arr,都是函数内部的变量,因此其空间都在main方法对应的栈帧中分配。
a、b是内置类型的变量,因此其空间中保存的就是给该变量初始化的值。
array是数组类型的引用变量,其内部保存的内容可以简单理解成是数组在堆空间中的首地址。
array1=array2;
让array1去引用array2引用的空间,此时array1和array2完全是一个数组。
在方法中修改参数的值(基础数据),不影响实参的值。
修改引用数据类型的值,会影响实参的值
所谓的 “引用” 本质上只是存了一个地址. Java 将数组设定成引用类型, 这样的话后续进行数组参数传参, 其实只
是将数组的地址传入到函数形参中. 这样可以避免对整个数组的拷贝(数组可能比较长, 那么拷贝开销就会很大).
有些知识点和c++没差 我就不写了哈 感觉好啰嗦 摆烂
public class Date {
public int year;
public int month;
public int day;
//无参构造方法--内部给各个成员赋值初始值,该功能与三个参数的构造方法重复
//此处可以在无参构造方法中通过this调用带有三个参数的构造方法
//但是this(1900,1,1);必须是构造方法中第一条语句
public Date(){
this(1900,1,1);
//this.year=1900;
//this.month=1;
//this.day=1;
}
public Date(int year,int month,int day){
this.year=year;
this.month=month;
this.day=day;
}
public static void mian(String[] args){
}
}
this(…)必须是构造方法中第一条语句
不能形成环
public Date(){
this(1900,1,1);
}
public Date(int year, int month, int day) {
this();
}
/*
无参构造器调用三个参数的构造器,而三个参数构造器有调用无参的构造器,形成构造器的递归调用
编译报错:Error:(19, 12) java: 递归构造器调用
*/
有个疑惑:局部变量必须要初始化才能使用,为什么字段声明之后没有给值依然可以使用?
public class Date {
public int year;
public int month;
public int day;
public Date(int year, int month, int day) {
// 成员变量在定义时,并没有给初始值, 为什么就可以使用呢?
System.out.println(this.year);
System.out.println(this.month);
System.out.println(this.day);
}
public static void main(String[] args) {
// 此处a没有初始化,编译时报错:
// Error:(24, 28) java: 可能尚未初始化变量a
// int a;
// System.out.println(a);
Date d = new Date(2021,6,9);
}
}
要搞清楚这个过程,就需要知道 new 关键字背后所发生的一些事情:
Date d = new Date(2021,6,9);
在程序层面只是简单的一条语句,在JVM层面需要做好多事情,下面简单介绍下:
数据类型 | 默认值 |
---|---|
byte | 0 |
char | ‘\u0000’ |
short | 0 |
int | 0 |
long | 0L |
boolean | false |
float | 0.0f |
double | 0.0 |
reference | null |
设置对象头信息(关于对象内存模型后面会介绍)
调用构造方法<>,给对象中各个成员赋值
public class Date {
public int year = 1900;
public int month = 1;
public int day = 1;
public Date(){
}
public Date(int year, int month, int day) {
}
public static void main(String[] args) {
Date d1 = new Date(2021,6,9);
Date d2 = new Date();
}
}
代码编译完成后,编译器会将所有给成员初始化的这些语句添加到各个构造函数中
Java中主要通过类和访问权限来实现封装:类可以将数据以及封装数据的方法结合在一起,更符合人类对事物的认
知,而访问权限用来控制方法或者字段能否直接在类外使用。Java中提供了四种访问限定符:
protected主要是用在继承中,继承部分详细介绍
default权限指:什么都不写时的默认权限
访问权限除了可以限定类中成员的可见性,也可以控制类的可见性
在面向对象体系中,提出了一个软件包的概念,即:为了更好的管理类,把多个类收集在一起成为一组,称为软件包。有点类似于目录。比如:为了更好的管理电脑中的歌曲,一种好的方式就是将相同属性的歌曲放在相同文件下,也可以对某个文件夹下的音乐进行更详细的分类。
在Java中也引入了包,包是对类、接口等的封装机制的体现,是一种对类或者接口等的很好的组织方式,比如:一个包中的类不想被其他包中的类使用。包还有一个重要的作用:在同一个工程中允许存在相同名称的类,只要处在不同的包中即可。
例如:使用java.util.Date导入java.util这个包中的Date类
public static void main(String[] args) {
java.util.Date data=new java.util.Date();
//得到一个毫秒级别的时间
System.out.println(data.getTime());
}
直接import语句导入一个具体的类,不能导入一个具体的包
import java.util.Arrays;//需要导入一个具体的类
import java.util;//不能导入一个具体的包
import java.util.Date;
public class{
public static void main(String[] args) {
Date data=new Date();
System.out.println(data.getTime());
}
}
引用java.util.这个包其中的其他类时
虽然但是还是推荐显示的使用,因为还是会发生冲突(包中类的方法相同)
import java.util.*;//这个表示导入util中所有的类,但是会冲突
public class Test {
public static void main(String[] args) {
Date date = new Date();
// 得到一个毫秒级别的时间戳
System.out.println(date.getTime());
}
}
所以下面这种情况下需要写完整类名
import java.util.*;//通配符
import java.sql.*;
public class Test {
public static void main(String[] args) {
java.util.Date date = new java.util.Date();
System.out.println(date.getTime());
}
}
可以使用import static导入包中静态的方法和字段。
lang下的包System的out属性
import static java.lang.Math.*;
import static java.lang.System.*;
public class Test {
public static void main(String[] args) {
//System.out.println("fasfa");
//直接可以省略System,写out
//用得少
out.println("fsfsdga");
//又例如
out.println(Math.max(10,20));
//max是类名去调用的静态方法
//所以开头加入
//import static java.lang.Math.*;
//out.println(max(10,20))
double x = 30;
double y = 40;
// 静态导入的方式写起来更方便一些.
// double result = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
double result = sqrt(pow(x, 2) + pow(y, 2));
System.out.println(result);
}
}
包是组织类的一种形式,使用包是为了保证类的唯一性
import 和 C++ 的 #include 差别很大. C++ 必须 #include 来引入其他文件内容, 但是 Java 不需要.
import 只是为了写代码的时候更方便. import 更类似于 C++ 的 namespace 和 using
包实际是一个文件夹 src放的源代码
包名必须是小写字母
在弹出的对话框中输入包名, 例如 com.bit.demo1
在包中创建类, 右键包名 -> 新建 -> 类, 然后输入类名即可
注意:
private包访问权限-》只能在当前包中使用,当你的成员变量不加任何访问修饰限定词的时,默认使用
这是由于这两个类是在同一个包底下
所以test类不能在不同包中使用 即使import导入包中的类
常见的系统包
几个问题:
其中:Animal类称为父类/基类或超类,Dog和Cat可以称为Animal的
子类/派生类,继承之后,子类可以复用父类中成员,子类在实现时只需关心自己新增加的成员即可。
语法规则
class A{
int a=10;
}
class B extends A{
char a ;
public void func(){
a=97;//char类型可以用整数接收
}
//仍然满足就近原则 优先子类自己的
//即子类将父类同名成员隐藏了
}
public class TestDemo {
public static void main(String[] args) {
B b = new B();
b.func();
System.out.println(b.a);//输出为a
}
}
如何换用父类
class A{
int a=10;
}
class B extends A{
char a ;
public void func(){
super.a=97;
}//引用父类的a,所以此时char a没有定义
}
public class TestDemo {
public static void main(String[] args) {
B b = new B();
b.func();
System.out.println(b.a);//输出空字符
A a = new A();
System.out.println(a.a);//输出10
}
}
【相同点】
【不同点】
在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性
this是非静态成员方法的一个隐藏参数,super不是隐藏的参数
成员方法中直接访问本类成员时,编译之后会将this还原,即本类非静态成员都是通过this来访问的;在子类
中如果通过super访问父类成员,编译之后在字节码层面super实际是不存在的(通过字节码文件可以验证)
构造方法中一定会存在super(…)的调用,用户没有写编译器也会增加,但是this(…)用户不写则没有
class Animal{
public String name;
public int age;
//执行顺序:先静态后实例后构造方法
static{
System.out.println("这是Animal的静态代码块");
}
{
System.out.println("这是Animal的实例代码块");
}
public Animal(String name, int age){
this.name=name;
this.age=age;
System.out.println("Animal带两个参数的构造方法");
}
public Animal() {
name="子类无参优先帮助父类构造";
age=889;
System.out.println("Animal不带参数的构造方法");
}
}
class Cat extends Animal{
public float weight;
static{
System.out.println("这是cat的静态代码块");
}
{
System.out.println("这是cat的实例代码块");
}
public Cat(){
super();
//name="dada";
//age=12;
//子类优先
weight=2;
System.out.println("不带参数的构造方法");
}
public Cat(String name, int age,float weight){
super(name,age);
this.weight=weight;
System.out.println("cat带三个参数的构造方法");
}
}
public class TestDemo {
public static void main(String[] args) {
Cat cat=new Cat("kk",11,2);
//cat.show();
System.out.println("======");
Cat cat2=new Cat("mm",11,22);
}
}
[输出结果]
这是Animal的静态代码块
这是cat的静态代码块
这是Animal的实例代码块
Animal带两个参数的构造方法
这是cat的实例代码块
cat带三个参数的构造方法
======
这是Animal的实例代码块
Animal带两个参数的构造方法
这是cat的实例代码块
cat带三个参数的构造方法
通过分析执行结果,得出以下结论:
1、父类静态代码块优先于子类静态代码块执行,且是最早执行
2、父类实例代码块和父类构造方法紧接着执行
3、子类的实例代码块和子类构造方法紧接着再执行
4、第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行
父类中private成员变量虽然在子类中不能直接访问,但是也继承到子类中了
什么时候下用哪一种呢?
我们希望类要尽量做到 “封装”, 即隐藏内部实现细节, 只暴露出 必要 的信息给类的调用者.
因此我们在使用的时候应该尽可能的使用 比较严格 的访问权限. 例如如果一个方法能用 private, 就尽量不要
用 public.
另外, 还有一种 简单粗暴 的做法: 将所有的字段设为 private, 将所有的方法设为 public. 不过这种方式属于是
对访问权限的滥用, 还是更希望同学们能写代码的时候认真思考, 该类提供的字段方法到底给 “谁” 使用(是类内
部自己用, 还是类的调用者使用, 还是子类使用).
可修饰变量,成员方法以及类
final int a = 10;
a = 20; // 编译出错
final public class Animal {
…
}
public class Bird extends Animal {
…
}
// 编译出错
Error:(3, 27) java: 无法从最终com.bit.Animal进行继承
class Money{
}
class Student{
public Money money=new Money();
}
class Teacher{
public Money money;//复用money类中的属性和方法
}
class School{
public Money money;
public Teacher[] teacher;
public Student[] student;
}
父类对象引用子类
实际上就是创建一个子类对象,将其当作父类对象来使用
父类类型 对象名 =new 子类类型
Animal animal=new Cat(“姐姐”,2);
animal是父类类型,但是可以引用一个字类类型,因为是从小范围到大范围的转换。
三种常见的父类转型
1.直接赋值
class Animal{
public String name;
public int age;
public void eat(){
System.out.println(this.name+"eat");
}
}
class Cat extends Animal{
public String hair;
public void mew(){
System.out.println(this.name+"mew");
}
}
public class TestDemo {
public static void main2(String[] args) {
//Cat cat = new Cat();
//Animal animal = cat;//父类的引用可以引用子类的对象
//合并成一句话
Animal animal = new Cat();//向上转型
//animal.hair="wqe";错误
animal.name="kk ";
animal.eat();
//animal.mew();错误 不可以访问 只能访问父类自己特有的成员
}
public static void main1(String[] args) {
Cat cat=new Cat();
cat.mew();
cat.eat();
Animal animal = new Animal();
animal.eat();//这里通过父类引用 只能访问父类自己的方法
}
}
2.方法传参
public class TestDemo {
public static void func(Animal animal){
}
public static void main(String[] args) {
//Cat cat = new Cat();
//func(cat);
//或者
func(new Cat());
}
}
3.方法返回
public class TestDemo {
public static Animal func(){
//return new Animal();
//或者
return new Cat();
//此就是应用了向上转型,子类对象当成父类对象来使用
}
}
向上转型的优点:让代码实现更简单灵活。
向上转型的缺陷:不能调用到子类特有的方法
将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的方法,此时:将父类引用再还原为子类对象即可,即向下转换
向下转型引用对象 = (转型的类型)父类引用;
父类引用 instanceof 转型类型
public static void main(String[] args) {
Animal animal = new Cat();//向上转型
//由于向下转型不安全,不能确定该引用对象是不是Bird类的实例化对象
//即猫不是鸟
//instanceof ::用来检查向下转型是否安全,不安全返回false
if (animal instanceof Bird) {
Bird bird = (Bird) animal;//向下转型
bird.fly();
}
}
#3# 10.3 方法重写
class Animal{
public String name="动物";
public int age;
public void eat(){
System.out.println(this.name+"吃饭");
}
}
class Cat extends Animal{
public String hair;
public void eat(){
System.out.println("mao吃猫粮");
}
public void mew(){
System.out.println(this.name+"mew");
}
}
public class TestDemo {
public static void main(String[] args) {
Animal animal = new Cat();
animal.eat();//实际上编译的还是这里还是Animal的eat()方法
}
}
运行结果:
animal调用的是子类的方法
但是此时任然不能访问mew(),父类没有
为什么会调用子类的呢?
实际上编译的animal.eat()还是Animal的eat()方法
找到字节码文件目录,用PowerShell打开字节码文件进行编译
使用命令行语句:
但是在运行的时候就变成了子类自己的eat(),运行时绑定,在运行时才确定要绑定谁,称动态绑定;
静态绑定:也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用那个方法。典型代表函数重载。
动态绑定:也称为后期绑定(晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体调用那个类的方法。
要求
注意
协变类型
比如返回值类型构成父子关系
具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。
在java中要实现多态,必须要满足如下几个条件,缺一不可:
public static void function(Animal animal){
animal.eat();
}
public static void main(String[] args) {
Cat cat = new Cat();
Dog dog = new Dog();
function(cat);
function(dog);
}
避免在构造方法中调用重写的方法
class B {
public B() {
// 此时也会发生动态绑定,转为调用子类的func方法
func();
}
public void func() {
System.out.println("B.func()");
}
}
class D extends B {
private int num = 1;
D(){
super();
}
@Override
public void func() {
System.out.println("D.func() " + num);
}
}
public class Test {
public static void main(String[] args) {
D d = new D();
}
}
执行结果:
D.func() 0
分析:
构造d对象的同时会调用父类的构造方法
b的构造方法中还调用了func方法,此时会触发动态绑定,会调用到d中的func
此时d对象自身还没有完成构造,所以num处于未初始化的状态,值为0;
结论: “用尽量简单的方式使对象进入可工作状态”, 尽量不要在构造器中调用方法(如果这个方法被子类重写, 就会触
发动态绑定, 但是此时子类对象还没构造完成), 可能会出现一些隐藏的但是又极难发现的问题.
//抽象类
abstract class Shape{
public abstract void draw();//抽象方法
}
class Cycle extends Shape{
//当一个普通的类,继承了这个抽象类之后,那么这个普通类必须重写这个抽象类当中的所有抽象方法
@override
public void draw(){
System.out.println("00");
}
}
//当一个抽象类A继承了抽象类B,此时抽象类不必要重写抽象类B中的方法
abstract class A extends Shape{
public abstract void func();
}
//当一个普通类接着继承,此时就得重写所有的抽象方法
class C extends A{
@Override
public void func() {
}
@Override
public void draw() {
}
}
public class TestDemo {
public static void main(String[] args) {
//抽象类不能实例化
//Shape shape = new Shape();错误
//抽象类存在的最大意义就是额为了被继承
//且抽象类必须被继承
//所以抽象类也可以发生向上转型,进一步发生多态
Shape shape = new Cycle();
}
}
唯一的和普通类的区别就是被abstract修饰,不能进行实例化
注意:
【抽象类的作用】
抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类. 然后让子类重写抽象类中的抽象方法.
有些同学可能会说了, 普通的类也可以被继承呀, 普通的方法也可以被重写呀, 为啥非得用抽象类和抽象方法
呢?
确实如此. 但是使用抽象类相当于多了一重编译器的校验.
使用抽象类的场景就如上面的代码, 实际工作不应该由父类完成, 而应由子类完成. 那么此时如果不小心误用成父类
了, 使用普通类编译器是不会报错的. 但是父类是抽象类就会在实例化的时候提示错误, 让我们尽早发现问题.
很多语法存在的意义都是为了 “预防出错”, 例如我们曾经用过的 final 也是类似. 创建的变量用户不去修改, 不
就相当于常量嘛? 但是加上 final 能够在不小心误修改的时候, 让编译器及时提醒我们.
充分利用编译器的校验, 在实际开发中是非常有意义。
接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用。
在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型。
2.2 语法规则
接口的定义格式与定义类的格式基本相同,将class关键字换成 interface 关键字,就定义了一个接口。
interface IShape{
//成员变量 默认为public static final
public static final int a = 10;
//成员方法 默认为public abstract
public abstract void draw();
//接口当中的方法,如果要实现,需要用default来修饰
default void func(){
System.out.println("默认的方法");
}
//接口当中静态的方法可以有具体的实现
public static void staticFunc(){
System.out.println("静态方法");
}
}
//接口被继承 implement
//重写接口的方法
//一个普通的类可以通过implement来实现这个接口
// 需要重写抽象方法,覆盖父类的
// 也可以重写默认方法
class A1 implements IShape {
@Override
public void draw() {
System.out.println("子类继承后重写的方法");
}
}
public class Tst {
//接口可以实现向上转型,也可以实现多态
public static void drawMap(IShape shape){
shape.draw();
}
public static void main(String[] args) {
//接口不能进行实例化,只能被继承
//IShape iShape = new IShape();
IShape iShape = new A1();
iShape.draw();
iShape.func();
IShape.staticFunc();
IShape iShape1 = new A1();
drawMap(iShape1);
}
}
提示:
接口不能直接使用,必须要有一个"实现类"来"实现"该接口,实现接口中的所有抽象方法。
public class 类名称 implements 接口名称{
// …
}
注意:子类和父类之间是extends 继承关系,类与接口之间是 implements 实现关系。
注意:子类和父类之间是extends 继承关系,类与接口之间是 implements 实现关系。
(一个接口一个Java文件 一个类一个Java文件)
一个类可以继承抽象类,同时实现多个接口,每个接口之间使用逗号隔开
为什么接口的出现解决了多继承的问题?
接口可以相当于一个特性 只要是参数,具备该种特性就可以用implement实现接口
class Animal{
public String name;
public int age;
public Animal(String name){
this.name = name;
}
public void eat(){
System.out.println(this.name+"吃饭");
}
//不能把飞 跑 游泳这些操作 写道父类(标准)中去
}
interface IFlying{
public abstract void fly();
}
interface IRunning{
void run();
}
interface ISwimming{
void swim();
}
//狗继承动物实现跑 游泳接口 同时也必须重写接口中所有的抽象方法
class Dog extends Animal implements IRunning,ISwimming{
public Dog (String name){
super(name);
}
@Override
public void run() {
System.out.println("狗跑步");
}
@Override
public void swim(){
System.out.println("狗游泳");
}
}
//1.构造方法需要调用父类方法·实例化(构造)猫这个类型
class Cat extends Animal implements IRunning{
public Cat (String name){
super(name);
}
@Override
public void run() {
System.out.println("猫跑步");
}
}
public class Test2 {
public static void walk(IRunning iRunning){
iRunning.run();
}
public static void main(String[] args) {
walk(new Dog("ww"));
walk(new Cat("mi"));
}
public static void main1(String[] args) {
Animal animal = new Cat("mi");
Animal animal2 = new Dog("wang");
IRunning iRunning = new Dog("wangwang");
IRunning IRunning2 = new Cat("mimi");
ISwimming swimming = new Dog("nini");
}
}
interface W{
void funcW();
}
interface M{
void funcM();
}
interface N extends W,M{
void funcN();
}
//或者直接在这前面加abstract就不需要全部重写抽象函数 但是当另一个类再次继承ww时候必须全部重写抽象函数
class WW implements N{
@Override
public void funcW(){
System.out.println("jiejie");
}
@Override
public void funcM() {
}
@Override
public void funcN() {
}
}
java interface Comparrable
package com.sxt.demo3;
import java.util.Arrays;
import java.util.Comparator;
/**
* Created with Intellij IDEA
* Discripution:3个重要的接口
* User:ALL
* Date:2022-05-S{DAY}
* Time:20:56
*/
//comparable接口
//相当于将每个对象强转于comparable,comparable中的函数只有compareTo;
//即将每个对象的其它信息隐藏只留下compareTo中指定的属性来进行比较
//
class Student i/*mplements Comparable*/ {
public String name;
public int age;
public double score;
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
@Override
public String toString() {
return "Student{" + "name=" + name + '\'' +
",age=" + age + "\'" +
",score=" + score + "}";
}
/* @Override
public int compareTo(Student o) {
return this.name.compareTo(o.name);
//return this.age-o.age;
}*/
}//
//comparator接口
//comparable接口只能一个一个的重新比较
//comparator接口可以由多个对象继承。每个对象的属性都可以重写接口
//*问题 为什么不需要实现comparator接口不用重写equals抽象方法?
//一个类扩展了一个超类,同时实现了一个接口,并从超类和接口继承了相同的方法,
// 在这种情况下,只会考虑超类的方法,接口无论是否提供默认方法都会被忽略,这正是“类优先”规则。
//equals方法在Object超类中有(已实现),在Comparator接口中也有(未实现),
// 所以一个类实现Comparator接口(实际隐含继承Object超类),
// 故根据“类优先”规则,会忽略Comparator接口中的equals方法,故而只需实现compare方法即可。
//比较器
class AgeComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2){
return o1.age-o2.age;
}
}
class StringComparator implements Comparator<Student>{
@Override
public int compare(Student o1,Student o2){
return o1.name.compareTo(o2.name);
}
}
class ScoreComparator implements Comparator<Student>{
@Override
public int compare(Student o1,Student o2){
return (int) (o1.score-o2.score);
}
}
public class Tesr {
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("syh", 22, 9);
students[1] = new Student("sh", 2, 8);
students[2] = new Student("s", 0, 7);
/*System.out.println(students[0].compareTo(students[1]));
Arrays.sort(students);
System.out.println(Arrays.toString(students));*/
//java中无法通过大于小于号来引用对象进行比较
//if(students[0]>students[1])
//*Java中的引用类型比较大小的前提,
//1.可以比较
//2.重写某种方法(重写compareTo或者比较器中的compare方法)
//this.compareTo.o
//students[0].compareTo(students[1])
System.out.println("排序前"+Arrays.toString(students));
AgeComparator agecomparetor = new AgeComparator();
StringComparator stringComparator=new StringComparator();
Arrays.sort(students,stringComparator);
//public static void sort( @NotNull T[] a,
// @Nullable java.util.Comparator super T> 0
System.out.println("排序后"+Arrays.toString(students));
}
public static void main1(String[] args) {
int[] Arr = {1,222,43,42};
Arrays.sort(Arr);
System.out.println(Arrays.toString(Arr));
}
}
package com.sxt.demo4;
/**
* Created with Intellij IDEA
* Discripution:clonable接口和深拷贝
* User:ALL
* Date:2022-05-S{DAY}
* Time:17:27
*/
class Money{
public double money = 19.9;
}
//拷贝数组
//person1引用所指的对象是id,现在要拷贝对象,克隆方法
class Person implements Cloneable{
//基本类型
public int id =1234;
//引用类型
public Money M = new Money();
@Override
public String toString(){
return "id:"+id;
}
@Override
protected Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException{
Person person1=new Person();
//*拷贝对象前提
//1.这个对象可以被克隆
//2.自定义类型被克隆需要实现Cloneable接口
//没有new对象,是克隆出来的,会产生一个所指对象的副本(地址不同)
Person person2=(Person)person1.clone();
System.out.println(person1.M.money);//19.9
System.out.println(person2.M.money);//19.9
System.out.println("=====");
person1.M.money=99.9;
System.out.println(person2.M.money);//99.9
System.out.println(person2.M.money);//99.9
}
}
class Money implements Cloneable{
public double money = 19.9;
@Override
public Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
//拷贝数组
//person1引用所指的对象是id,现在要拷贝对象,克隆方法
class Person implements Cloneable{
//基本类型
public int id =1234;
//引用类型
public Money M = new Money();
@Override
public String toString(){
return "id:"+id;
}
@Override
protected Object clone() throws CloneNotSupportedException{
//浅拷贝
// return super.clone();
//深拷贝
Person tmp=(Person)super.clone();
tmp.M=(Money)this.M.clone();
return tmp;
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException{
Person person1=new Person();
//*拷贝对象前提
//1.这个对象可以被克隆
//2.自定义类型被克隆需要实现Cloneable接口
//没有new对象,是克隆出来的,会产生一个所指对象的副本(地址不同)
Person person2=(Person)person1.clone();
System.out.println(person1.M.money);//19.9
System.out.println(person2.M.money);//19.9
System.out.println("=====");
person2.M.money=99.9;
System.out.println(person1.M.money);//浅:99.9 深:19.9
System.out.println(person2.M.money);//99.9
}
}
实现深拷贝 是从代码层次上进行的,不是说某个方法是深拷贝,而是从代码的实现上来看的
this.name.equals(Student.name);
class Money implements Cloneable{
public double money = 19.9;
@Override
public Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
//拷贝数组
//person1引用所指的对象是id,现在要拷贝对象,克隆方法
class Person implements Cloneable{
//基本类型
public int id =1234;
//引用类型
public Money M = new Money();
@Override
public String toString(){
return "id:"+id;
}
@Override
protected Object clone() throws CloneNotSupportedException{
//浅拷贝
// return super.clone();
//深拷贝
Person tmp=(Person)super.clone();
tmp.M=(Money)this.M.clone();
return tmp;
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException{
Person person1=new Person();
//*拷贝对象前提
//1.这个对象可以被克隆
//2.自定义类型被克隆需要实现Cloneable接口
//没有new对象,是克隆出来的,会产生一个所指对象的副本(地址不同)
Person person2=(Person)person1.clone();
System.out.println(person1.M.money);//19.9
System.out.println(person2.M.money);//19.9
System.out.println("=====");
person2.M.money=99.9;
System.out.println(person1.M.money);//浅:99.9 深:19.9
System.out.println(person2.M.money);//99.9
}
}
[外链图片转存中…(img-CMaxeYOY-1654932797589)]
实现深拷贝 是从代码层次上进行的,不是说某个方法是深拷贝,而是从代码的实现上来看的