一、final 关键字
final修饰属性、方法、类
-
修饰属性:修饰的属性是常量,必须直接赋值、或在构造方法中赋值
-
定义时直接赋值
static final int num =10; //常量不可变 ,所以直接写在模板上,故而 static final 推荐一起使用
-
定义时,没有赋值,在构造方法中赋值,在每个对象中都可以拥有一个常量
final int count ; //在构造方法中赋初值
public FinalDemo ( int count ){ // 每新建一个对象,就为属性赋值,赋值后,值就不能改变
this.count = count;
}
-
-
修饰方法:子类中不可以重写
-
修饰类 :不可以被继承, 不能修饰为抽象类或接口
package com.feifan.javaoop.day5;
/*
final 修饰 类、属性、方法
修饰类: 不能修饰抽象类、接口,修饰的类不能被继承
修饰方法: 不能修饰抽象方法,修饰的方法不能被子类重写
修饰属性: 修饰后的属性是 常量,值不可以改变,必须初始化赋值 或 初试化构造方法赋值
1)直接初始化赋值 值就在方法区里的模板上,值是确定的,大家共享, 推荐使用static final
2)初始化构造方法赋值 每新建一个对象,就为属性赋值,赋值后值就不能改变了
*/
public class FinalDemo{
// final修饰类 public final class String{}
// final修饰属性
// ctrl + 鼠标左键 进入底层查看String代码 private final char value[];
String s = "abc"; //字符串在底层是以数组形式存储 char [a,b,c] 字符串的值不能改变
//属性在定义时直接赋值
final int num = 10; //值不可以改变,推荐static final组合使用,整个内存中存一份
//属性在定义时没有为常量赋值,使用构造方法赋值,在每个对象中都可以拥有一个常量使用
final int count;//每创建一个对象时,为常量count赋值
public FinalDemo(int count) {
this.count = count;
}
public void test(){
//测试final修饰的num值能否改变
//num =100; 报错
new String("a");
new FinalDemo(10); //测试在定义时没有为属性赋值,通过构造方法赋值
new FinalDemo(20); //每新建一个对象时,给属性赋值,赋值后就不能改变
}
/* //final修饰方法 不能被子类重写 final不能修饰构造方法
public final void test(){
//测试final修饰的方法能否被子类重写
}*/
}
二、接口 (interface、 implement)
1、什么是接口?
- 顶层的功能定义 ,专注于功能设计,而不是实现,让其他类具体实现
- 与抽象类相似,但有区别,接口实现多继承的逻辑
- 用多态方法使用
1)生活中的接口----USB接口
-
USB接口本身没有实现任何功能
USB接口规定了数据传输的要求
USB接口可以被多种USB设备实现
只要有USB接口,就可以通过USB风扇,USB鼠标,U盘 具体实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dabXpqsb-1642950372831)(C:\Users\16431\AppData\Roaming\Typora\typora-user-images\image-20220123162439599.png)]
那怎么实现? 可以使用java接口来实现
-
编写USB接口---->通过需求设计方法 顶层 抽象的
-
实现USB接口---->实现所有方法
-
使用USB接口---->用多态方式使用 具体怎么用多态方式实现?
- 复习下多态:
- 多态使用场景: 喂不同动物吃,动物吃东西
- 多态三个条件: 继承、重写、父类引用指向子类对象
- 向上转型: 子类调用父类方法
- 向下转型:父类调用子类自身特有的方法
- 复习下多态:
2)面向接口编程
程序设计时:
- 关心实现类有何能力,而不关心实现细节
- 面向接口的约定而不考虑接口的具体实现
3)接口存在意义?
java中一个类只能有一个父类,所以接口可以实现多继承的逻辑
2、接口怎么用?
简单总结:
1)接口定义 ( interface )
[访问权限修饰符 ] interface 接口名称 [extends 其他接口名1,…其他接口名n]{
//1、静态属性 public static final
//2、抽象方法 public abstract
子类不能进行选择性地对所有抽象方法进行重写,要重写就得对所有抽象方法全部重写
//3、静态方法 public static void
//4、默认方法 public default void
子类可以选择是否对定义的默认方法重写
}
2)接口实现 ( implements )
-
[访问权限修饰符] class 类名 implements 接口1 ,接口2 …{ }
-
结合继承:
[访问权限修饰符] class 类名 extends 父类名 implements 接口1 ,接口2 …{ }
3)接口使用实例—Animal CanCry() CanFly()
- **接口是功能上的定义,**比如CanCry () CanFly()
- 抽象类除了功能定义还有类的成员变量,比如Animal 类中除了抽象方法还有成员变量
1)接口定义格式
-
接口——interface声明
-
接口中静态属性——默认public static final ,就是静态常量
接口中定义的属性用大写字母定义,比如:public static final PI =3.14;
-
接口中抽象方法——默认pubic abstract
-
接口中静态方法——接口可直接调用 public static
-
接口中默认方法——通过子类调用 public default
public interface MyInterface{ //interface 关键字定义类
int num; //所有属性默认为 public static final 静态常量
public void foo(); // 抽象方法是:public abstract
public static void test() {
//jdk8之后添加静态方法,接口可直接调用
}
public default void test1(){
//jdk8之后添加默认方法,通过子类调用
}
}
-
package com.feifan.javaoop.day5;
public interface MyInterface{
/*
1、接口中定义属性:
就是常量 用大写字母命名
默认public static final
*/
// public static final int NUM = 10; //必须赋值
int NUM = 10; //接口中定义的属性默认是静态常量
/*
2、接口中定义的方法:
默认public abstract
子类对抽象方法重写时,会对所有抽象方法重写
*/
// public abstract void eat();
void eat();
void sleep();
/*
//java8 之后, 新增了两种方法
3、静态方法
public static
4、默认方法
public default
*/
//静态方法 接口自己调用
public static void test1(){
System.out.println("test1是接口中的静态方法,"+"接口中的属性NUM="+NUM);
}
//默认方法 是让子类重写,或让子类调用, 子类可以有选择地对默认方法重写
public default void test2(){
System.out.println("test2是接口中的默认方法,");
}
}
//----------------------------------------------------------------------
package com.feifan.javaoop.day5;
public class TestMyInterface {
public static void main(String[] args) {
System.out.println(MyInterface.NUM);//测试接口中的属性
MyInterface.test1();//测试接口中的静态方法,通过接口名调用
}
}
2)接口实现格式
-
类使用 implements关键字来实现接口
- implements:实现
- [访问权限修饰符] class 类名 implements 接口1 ,接口2 …{ }
- 结合继承: [访问权限修饰符] class 类名 extends 父类名 implements 接口1 ,接口2 …{ }
-
实现接口的 类命名 ,公认的写法后加 Impl
- MyInterfaceImpl 前面:接口名,后面:Impl 表示接口类的实现
3、接口特性?
- 接口是隐式抽象的 ,声明接口时不需要abstract
- 接口中的方法可以是抽象的、静态的、默认的
- 接口中属性默认 public static final
- 接口不是被类继承,而是被类实现
- 接口不能实例化对象,无构造方法
- 一个类可以实现多个接口
- 与继承关系相似,接口与实现类之间存在多态
- 一个接口能继承其他多个接口
- 当类实现接口时,类要实现接口中的抽象方法,否则,类必须声明为抽象类
4、接口实例----Animal CanCry() CanFly()
**接口是功能上的定义,**比如CanCry () CanFly()
**抽象类除了功能定义还有类的成员变量,**比如Animal 类中除了抽象方法还有成员变量
package com.feifan.javaoop.day5.InterfaceDemo;
public abstract class Animal{
private String name;
public Animal() {
}
public Animal(String name) {
this.name = name;
}
//动物的共性 抽象类
public abstract void eat();
public abstract void sleep();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.feifan.javaoop.day5.InterfaceDemo;
public interface CanCry { // 动物功能 接口
void cry();
}
package com.feifan.javaoop.day5.InterfaceDemo;
public interface CanFly { // 动物功能 接口
void fly();
}
package com.feifan.javaoop.day5.InterfaceDemo;
public class Bird extends Animal implements CanFly, CanCry {
@Override
public void eat() { // 继承和接口实现 都需要重写方法
}
@Override
public void sleep() {
}
@Override
public void fly() {
}
@Override
public void cry() {
}
}
package com.feifan.javaoop.day5.InterfaceDemo;
public class Dog extends Animal {
@Override
public void eat() { // Dog没有implements CanCry,所以不能使用多态
}
@Override
public void sleep() {
}
}
package com.feifan.javaoop.day5.InterfaceDemo;
public class Test {
public static void main(String[] args) {
Animal bird = new Bird(); //多态
CanFly b = new Bird();//接口也可以实现多态方法
CanCry c = new Bird();
// CanCry d = new Dog();//错误 Dog类没有implements CanCry,所以不能通过CanCry声明
}
}
3、抽象类和接口区别
-
想实例 ,抽象类Animal 中有抽象方法eat() , sleep() ,这些是动物共性 。
然后设计接口CanCry CanFly 是一些动物实例对象的功能 。
比如鸟能叫,鸟能飞,而狗能叫,但狗不能飞, Bird 类 implements CanCry CanFly , 那么新建一个Brid对象时,就可以使用多态,接口引用 指向 Brid对象,类似于继承中的多态,通过子类调用父类中的方法(向上转型、向下转型),就可以通过新建的 Brid对象 实现接口 。
-
**接口是功能上的定义,**比如CanCry () CanFly()
-
抽象类除了功能定义还有类的成员变量,比如Animal 类中除了抽象方法还有成员变量
1)相同点:
- 都不能创建对象
- 都可以表示多态性 (座位父类)
2)不同点:
- 接口:
- 接口不能有构造方法
- 不能有成员变量,只能有静态属性
- 不能有成员方法,只能有静态方法,默认方法
- 抽象类
- 抽象类能有构造方法
- 能有成员变量
4、抽象类和接口的关系?
-
一个类只能继承一个父类 ---- 类和类
-
一个类可以实现多个接口 ---- 类和接口
-
一个接口可以继承多个接口 ----- 接口和接口
package com.feifan.javaoop.day5;
public class InterfaceA {
}
package com.feifan.javaoop.day5;
public class InterfaceB {
}
package com.feifan.javaoop.day5;
public class InterfaceC {
}
package com.feifan.javaoop.day5;
public interface MyInterface extends InterfaceB, InterfaceC{
}
package com.feifan.javaoop.day5;
/*
一个类只能继承一个父类
一个类可以实现多个接口
一个接口可以继承多个接口
*/
public class MyInterfaceImpl extends Object implements MyInterface, InterfaceA{
/*
一个类实现接口,要么将此类声明为抽象类
要么重写接口中的所有抽象方法
*/
/*
MyInterfaceImpl 类 实现 InterfaceA 和 MyInterface 两个接口,
而 MyInterface接口又继承 InterfaceB,InterfaceC 两个接口,
所以 MyInterfaceImpl 相当于实现 InterfaceA、 MyInterface、 InterfaceB、和 InterfaceC 四个接口
所以 MyInterfaceImpl 要对 四个接口中的方法都进行重写
从而实现一个类 多继承 的逻辑
*/
@Override
public void eat() {
}
@Override
public void sleep() {
}
/*
默认方法可以重写,也可以不重写
*/
@Override
public void test2() {
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nELZC0Gj-1642950372834)(C:\Users\16431\AppData\Roaming\Typora\typora-user-images\image-20220123184643345.png)]
三、数组
1、什么是数组?
- 数组是一组数据类型相同的数据集合
- 数组是引用数据类型,即 数组是对象
- 数组创建时必须有指定长度,创建后长度不可变,空间中内存是连续的
- 数组可以存储基本数据类型,也可以存储引用数据类型
面试题:现有5MB大小内存空间,能否创建3MB大小的内存空间?
答案:不一定 , 5M空间中可能没有连续的3M, 数组必须是连续的内存空间
2、 数组的声明和创建
1)数组声明
- int [] m; //建议使用这种方式
- int n [];
2)数组创建
- int [] a = new int[5];
- int [] a1 = new int[]{1,2,3,4,5};
- int [] a3 = {1,2,3,4,5};
package com.ffyc.javaarray;
import java.util.Arrays;
public class Demo {
/*
数组:一组数据 一组数据类型相同的数据
数组是对象,里面可以有指定长度的连续空间
数组内存空间必须是连续的
创建时指定长度
面试题:现有5MB大小内存空间,能否创建3MB大小的内存空间?
答案:不一定 , 5M空间中可能没有连续的3M
*/
public static void main(String[] args) {
/* 声明数组:2种方式
int 表示数组对象中可以存储的数据类型是int
[] 表示是数组
a 就是数组对象的引用
*/
int [] m; //建议使用这种方式
int n [];
/* 创建数组:3种方法
动态创建数组(没有为数组赋值,可以结合for循环赋值)1、 静态创建数组(创建时就赋初值)2、3
数组是对象
创建数组必须指定长度,数组长度一旦定义,就不能改变,因为数组空间必须是连续的
数组可以是数据类型,也可以是引用类型
*/
//1、创建了数组对象,分配了空间,为空间分配了默认值,后期再对数组赋值操作
int [] a = new int[5];
long [] b = new long[5];
char [] c = new char[5];
boolean [] d = new boolean[5];
String [] s = new String[5];
System.out.println(a.length); //10
System.out.println(a); //[I@1b6d3586 输出的是地址,默认调用Objec类中的toString(),将对象以字符串形式展示
System.out.println(Arrays.toString(a)); //[0, 0, 0, 0, 0] toString 将数组内容以字符串形式输出
System.out.println(Arrays.toString(b)); //[0, 0, 0, 0, 0]
System.out.println(Arrays.toString(c)); //[ , , , , ]
System.out.println(Arrays.toString(d)); //[false, false, false, false, false]
System.out.println(Arrays.toString(s)); //[null, null, null, null, null]
//2、创建数组对象时,直接对数组内容进行填充
int [] a1 = new int[]{1,2,3,4,5};
System.out.println(a1);//[I@4554617c
System.out.println(Arrays.toString(a1));//[1, 2, 3, 4, 5] 将数组内容以字符串形式输出
//3、数组内容已知,就可以简化
int [] a3 = {1,2,3,4,5};
System.out.println(a3);//[I@74a14482
System.out.println(Arrays.toString(a3));//[1, 2, 3, 4, 5]
}
}
3、 数组的访问和迭代
1)数组元素的访问
- 数组名 [ index ] index 索引、下标
- 索引从0开始
- 索引最大值和数组长度差1
2)数组迭代
重复执行程序中的循环,直到满足某条件为止,亦称为迭代。
- 数组迭代 两种方式: for循环 、 增强for循环
for循环 和 增强for循环区别
- for 循环有下标 ,可以对数组内按数组下标访问
- 增强for循环 , 只能遍历出数组中的元素
package com.ffyc.javaarray;
import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;
import org.w3c.dom.ls.LSOutput;
public class Demo3 {
public static void main(String[] args) {
/*
对数组的遍历 --> 通过循环实现
数组索引 index
*/
int [] c = {1,2,4,3,5};
System.out.println(c.length);//通过length属性获取数组长度
for (int i = 0; i < c.length; i++) {
System.out.println(c[i]);
}
//jdk5 之后 推出了增强for循环,如果只需要遍历,可以使用增强for
/*
c 需要遍历的数组
int t 声明了一个变量
每次从数组c中,取出一个元素,赋给零时变量t
*/
for(int t : c){
System.out.println(t);
}
}
}
4、数组排序
1)冒泡排序
比较两个相邻的元素,将值 大/小 的元素交换到右边
package com.ffyc.javaarray;
public class BubbleSortDemo {
// 冒泡排序
//比较两个相邻的元素,将值大/小的元素交换到右边
public static void main(String[] args) {
int [] a = new int[]{1,4,3,5,6};
System.out.println("数组长度为:"+a.length);
System.out.println("排序前:");
for( int t : a ){ //增强for循环
System.out.print(t+"\t");
}
System.out.println("\n"+"排序后:");
BubbleSortDemo bubbleSortDemo = new BubbleSortDemo();
bubbleSortDemo.BubbleSort1(a, a.length);
System.out.println("从小到大排序:");
for (int i = 0; i < a.length; i++) {
System.out.print(a[i]+"\t");
}
System.out.println("\n"+"从大到小排序:");
bubbleSortDemo.BubbleSort2(a, a.length);
for (int i = 0; i < a.length; i++) {
System.out.print(a[i]+"\t");
}
}
//冒泡排序 从小到大
public void BubbleSort1(int a[],int lenght){
for (int j = 0; j < lenght -1; j++) {
for (int i = 0; i < lenght -1; i++) {
if (a[i] > a[i+1]){
int temp = a[i+1];
a[i+1] = a[i];
a[i] = temp;
}
}
}
}
//冒泡排序 从大到小
public void BubbleSort2(int a[],int lenght){
for (int j = 0; j < lenght -1; j++) {
for (int i = 0; i < lenght -1; i++) {
if (a[i] < a[i+1]){
int temp = a[i];
a[i] = a[i+1];
a[i+1] = temp;
}
}
}
}
}
数组长度为:5
排序前:
1 4 3 5 6
排序后:
从小到大排序:
1 3 4 5 6
从大到小排序:
6 5 4 3 1
2)选择排序
思想是: 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
package com.ffyc.javaarray;
public class SelectionSortDemo {
public static void main(String[] args) {
int [] a = {1,5,4,7,0,2,5};
System.out.println("数组长度为:"+a.length);
//排序前
System.out.println("排序前:");
for(int temp : a){
System.out.print(temp+"\t");
}
//排序后
System.out.println("\n从小到大排序:");
SelectionSortDemo selectionSortDemo = new SelectionSortDemo();
selectionSortDemo.SelectionSort1(a, a.length);
for (int i = 0; i < a.length; i++) {
System.out.print(a[i]+"\t");
}
System.out.println("\n从大到小排序:");
selectionSortDemo.SelectionSort2(a, a.length);
for(int temp : a){
System.out.print(temp+"\t");
}
}
//从小到大
public void SelectionSort1(int [] a, int length) {
for (int i = 0; i < a.length - 1; i++) {
int minIndex = i;
// 每一个元素都和剩下的未排序的元素比较
for (int j = i + 1; j < a.length; j++) {
if (a[j] < a[minIndex]) {
minIndex = j;//将最小数的索引保存
}
}
//经过一轮循环,就可以找出第一个最小值的索引,然后把最小值放到i的位置
swap(a, i, minIndex);
}
}
// 从大到小
public void SelectionSort2(int [] a, int length){
for (int i = 0; i < a.length - 1; i++) {
int maxIndex = i;
// 每一个元素都和剩下的未排序的元素比较
for (int j = i + 1; j < a.length; j++) {
if (a[j] > a[maxIndex]) {
maxIndex = j;//将最大数的索引保存
}
}
//经过一轮循环,就可以找出第一个最大值的索引,然后把最大值放到i的位置
swap(a, i, maxIndex);
}
}
public void swap(int [] a, int i, int j){
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
数组长度为:7
排序前:
1 5 4 7 0 2 5
从小到大排序:
0 1 2 4 5 5 7
从大到小排序:
7 5 5 4 2 1 0