Java课程期末复习&&学习总结

JAVA课程学习总结

知识点总结

Eclipse如何与GitHub连接
1.前期准备

GitHub:Aure219/JAVA-homework: JAVA-homework (github.com)

1选择Windows Preferences,选择Configuration,点击Add Entry

添加自己的name 和 email

image-20220303210711567

然后生成公钥私钥

复制一下

image-20220303211008704

接下来打开GitHub

点击Settings

image-20220303211133685

然后如图操作

image-20220303211308488

输入title和key

key就是之前复制的公钥

2.远程建库

远程仓库new一个

image-20220303202239843

取名

image-20220303202526435

复制SSH 等一下要用到

image-20220303202819128

3.本地操作

1.先新建一个Project

image-20220303203546386

2.然后new一个class

image-20220303203811912

取个名字,然后最好勾上圈出来的

image-20220303204015117

接下来写代码

提交到本地仓库

image-20220303204158992

image-20220303212628594

然后再commit

image-20220303204517716

点击你要上传的,通常后缀是.java

再点击2处的绿的加号

然后再3处随便写点东西

最后点最底部的push

image-20220303204613387

这时把刚才拷贝的粘贴到这里来

image-20220303205022142

image-20220303205555989

接下来就一直点,

出现下面这个页面应该就是成功了

image-20220303205646864

然后回到GitHub 发现上面出现了你的代码即可

image-20220303205733251

常用DOS命令

盘符切换:e:回车

查看当前路径下的内容:dir

进入单级目录:cd 目录

回退到上一级目录:cd…

进入多级目录:cd 目录1\目录2

回退到盘符目录:cd\

清屏:cls

退出命令提示符窗口:exit

hello world案例分析

1.先建一个文本文档,修改名称,后缀改成.java

2.用记事本打开,编写程序,并保存

3.编译运行

编译:javac 文件名.java

运行:java 文件名

1.打代码可以打一些,再打alt+/,

2.鼠标定位在某一行,按住shift+方向键,可以选取多行,ctrl+\是注释多行

1.输入什么,输出什么

		Scanner in =new Scanner(System.in);
		System.out.println("echo"+in.nextLine());//加前缀的话,要带上加号哦,加号起到连接字符串的作用,如果是print也是打印,只不过他不会自动换行

2.加法的使用

image-20220225145401863

加法的使用方法,是起到连接字符串的作用还是运算的作用

注意最上面加了一行代码import java.util.Scanner;(若使用了第十行,则要加这行代码)

数组

int [] arr1 = new int[10]

int [] arr1 = new int[n]

int [] arr1 = {1,2,3,4,5,6}

int [][] arr1 = new int[3][5]

int [] arr1 = new int[10];
arr1[0]=5;
int [] arr2 = arr1;
arr2[0]=16;
System.out.println(arr1[0]);//输出的是16
//因为arr1相当于管理者,而非数组本身,创建arr2,数组其实还是原来的数组,相当于arr1和arr2管理同一个数组
int[]a={
    
    1,2,3};
int[]b={
    
    1,2,3};
System.out.println(a==b);//输出false,尽管元素相同,因为a,b管理的两个不同的数组

String.format("%.3f",num) 表示保留三位小数 ,记住要加双引号哦

float a=8.2//编译失败,因为看到小数点,会自动认为是double类型,此时需要强制类型转换。

下面两种都可以

float a=8.2f;

float a=(float)8.2;

注意:这和C语言有点不一样的

保留两位小数并且向下取整

private static double getTwoDecimalPlaces(double number) {
    
    
	return (double) Math.floor(number*100)/100;
}

保留两位小数并向下取整,如果是向上取整,用ceil即可
如果要保留三位小数呢,*1000 / 1000 依此类推

多组输入

		Scanner input = new Scanner(System.in); 
		int a, b; 
		while(input.hasNextInt()){
    
    //重点在这里
			 a = input.nextInt();
			 b = input.nextInt(); 
			 System.out.println(a + b); 
			}

1.使用sqrt

Math.sqrt()

2 生成随机函数

(int) (Math.random()*10) 随机产生0到9

正则表达式

1 . 任意单个字符,除了换行符外

一个点就代表一个字符

String str="Java";
System.out.println(str.matches("..va"));//输出true
System.out.println(str.matches(".va"));//输出false	

2 (ab|cd) ab或者cd

String str="ten";
System.out.println(str.matches("t(en|im)"));//输出true
//单个字符或者多个字符也是可以的哦
String str1="Java";
System.out.println(str1.matches("J(a|v)va"));//输出true
System.out.println(str1.matches("(Jav|JJJ)a"));//输出true

3 [abc] a或b或c

String str="Java";
System.out.println(str.matches("Ja[uw]a"));//输出false
System.out.println(str.matches("Ja[uvw]a"));//输出true

4 [ [ [^abc ] ] ] 除了a、b或者c之外的任意字符

String str1="Java";	System.out.println(str1.matches("Ja[^abcdv]a"));//输出false
System.out.println(str1.matches("Ja[^abcd]a"));//输出true

5 [a-z] a到z

是区分大小写的,两端都是闭区间

String str1="Java";
System.out.println(str1.matches("Ja[A-V]a"));//输出false					    
System.out.println(str1.matches("Ja[a-v]a"));//输出true		

6 [a-e[m-p]] a到e或者m到p

String str1="Java";
System.out.println(str1.matches("[A-G[I-M]]av[a-d]"));//输出true

7 [a-e&&[c-p]] a到e与c到p的交集

String str1="Java";
System.out.println(str1.matches("[A-P[I-M]]av[a-d]"));//输出true

Tips 为什么正则表达式是\d,而敲的时候要\\d呢

因为反斜杠是一个特殊的字符,在字符串中开始转移序列,因此用\ \来表示\

8 \d 一位数字,等同于0-9

String str1="Java23";
System.out.println(str1.matches("Java[\\d][\\d]"));//输出true,两位数字就要用两次哦
System.out.println(str1.matches("Java\\d\\d"));//输出true,意外发现,不需要方括号也可以

9 \D 一位非数字

String str1="Java23";
System.out.println(str1.matches("[\\D]ava23"));//输出true		System.out.println(str1.matches("J\\Dva\\d\\d"));//输出true

10 \w 单词字符

单词字符是任意的字母,数字或者下划线字符。

11 \W 非单词字符

12 \s 空白字符

13 \S 非空白字符

14 p* 0次或者多次出现模式p

多个字母要加括号哦

String str1="ababa";
System.out.println(str1.matches("(ab)*"));//输出false
System.out.println(str1.matches("(ab)*a"));//输出true
String str2="ababab";
System.out.println(str2.matches("(ab)*"));//输出true
String str3="aaaaaa";
System.out.println(str3.matches("(ab)*"));//输出false
System.out.println(str3.matches("aaaaaa(ab)*"));//输出true

15 p+ 一次或者多次出现模式p

String str1="aaa";	
System.out.println(str1.matches("a+b*"));//输出true

16 p? 0次或者1次出现模式p

String str1="aaa";	
System.out.println(str1.matches("b?"));//输出false
System.out.println(str1.matches("aaab?"));//输出true

17 p{n} 正好出现n次模式p

18 p{n,} 至少出现n次模式p

19 p{n,m} n到m出现模式p

String str1="abb";	
System.out.println(str1.matches("a{1,9}bb"));//输出true,说明是包含区间端点的	System.out.println(str1.matches("a{2,9}bb"));//输出false

20 \p{P} 一个标点字符

String str1="j!erw";
System.out.println(str1.matches("j\\p{P}erw"));//输出true

示例:

1.社会安全号的模式为xxx-xx-xxxx,x是一位数字

String str1="111-22-2222";
//表示数字恰好出现了3次-恰好出现了2次-恰好出现了4次
System.out.println(str1.matches("[\\d]{3}-[\\d]{2}-[\\d]{4}"));//输出true

2.偶数表示

偶数是以数字0、2、4、6、8结尾

String str1="112";
System.out.println(str1.matches("[\\d]*[02468]"));//输出true,结尾是偶数就行了,因为前面的[\\d]*已经读取了前面的数字了

3 电话号码表示模式是(xxx)-xxx-xxxx, x代表一位数字,其中不能以0开头

String str1="(123)-123-1231";
System.out.println(str1.matches("\\([\\d]{3}\\)-[\\d]{3}-[\\d]{4}"));//输出true
String str1="(123-123-1231";
System.out.println(str1.matches("\\([\\d]{3}\\)-[\\d]{3}-[\\d]{4}"));//输出false,少括号不行哦

tips ()在正则表达式中是特殊字符,用于对模式分组。为了在正则表达式中表示字面值,必须使用 \ \

4.假定姓由最多25个字母组成,并且第一个字母为大写

String str1="Adsfds";
System.out.println(str1.matches("[A-Z][a-z[A-Z]]{1,24}"));//输出true

tips 不能随便将空白符放入到正则表达式中,会报错 ,编译都过不了

5 java标识符

标识符必须以字母,下划线或者以美元符号($)开头,不能以数字开头

标识符是一个由字母,数字,下划线或美元符号组成的字符序列

String str1="Adsfds$";
System.out.println(str1.matches("[a-z[A-Z][$]][\\w|$]*"));//输出true
继承

如果类C1继承自类C2,那么就将C1称为子类(又叫继承类或派生类),C2称为超类(又叫父类或基类)

public class GeometricObject {
    
    
	private String color = "white";
	private boolean filled;
	private java.util.Date dateCreated;//这是我没见过的
	
	public GeometricObject() {
    
    
		dateCreated = new java.util.Date();//获取对象创建的时间
	}
	
	public GeometricObject(String color,boolean filled){
    
    
		dateCreated = new java.util.Date();
		this.color = color;
		this.filled = filled;
	}
	
	public String getColor() {
    
    
		return color;
	}
	
	public void setColor(String color) {
    
    
		this.color = color;
	}
		
	public boolean isFilled() {
    
    
		return filled;
	}
	
	public void setFilled(boolean filled) {
    
    
		this.filled = filled;
	}
	
	public java.util.Date getDateCreated(){
    
    
		return dateCreated;//返回时间
	}
//表示Circle继承GeometricObject
public class Circle extends GeometricObject{
    
    
	private double radius;
	
	public Circle() {
    
    	
	}
	
	public Circle(double radius) {
    
    
		this.radius=radius;
	}  
	
    //重载的构造方法,通过调用setColor和setFilled方法来设置color和filled的属性,这两个方法是在父类中定义的
	public Circle(double radius,String color,boolean filled) {
    
    
		this.radius =radius;
		setColor(color);
		setFilled(filled);
        //this.color=color;
		//this.filled=filled;
        //这种写法错误,因为他们是GeometricObject类中的私有数据域,不能被其他任何类访问,唯一读取和改变color和filled的方法就是通过他们的获取方法和设置方法。
        
        //用super关键字可以改写成下面的代码
        //super(color,filled)
        //this.radius=radius
	}
	public double getRadius() {
    
    
		return radius;
	}
	
	public void setRadius(double radius) {
    
    
		this.radius=radius;
	}
	
	public double getArea() {
    
    
		return radius*radius*Math.PI;
	}
	
	public double getDiameter() {
    
    
		return 2*radius;
	}
	
	public double getPerimeter() {
    
    
		return 2*radius*Math.PI;
	}
	
	public void printCircle() {
    
    
		System.out.println("The circle is created "+getDateCreated()+" and the radius is "+radius);
	}
}

public class Rectangle extends GeometricObject{
    
    
private double width;
private double height;

public Rectangle() {
    
    
}

public Rectangle(double width,double height) {
    
    
	this.width=width;
	this.height=height;
}

public Rectangle(double width,double height,String color,boolean filled) {
    
    
	this.width=width;
	this.height=height;
	setColor(color);
	setFilled(filled);
}


public double getWidth() {
    
    
	return width;
}

public void setWidth(double width) {
    
    
	this.width=width;
}

public double getHeight() {
    
    
	return height;
}

public void setHeight(double height) {
    
    
	this.height=height;
}

public double getArea() {
    
    
	return width*height;
}

public double getPerimeter() {
    
    
	return 2*(width+height);
}
}

public class EX11_4 {
    
    
	public static void main(String[] args) {
    
    
		Circle circle=new Circle(1);
		System.out.println("A circle "+circle.toString());
		System.out.println("The color is "+circle.getColor());
		System.out.println("The radius is "+circle.getRadius());
		System.out.println("The area is "+circle.getArea());
		System.out.println("The diameter is "+circle.getDiameter());
		
		Rectangle rectangle=new Rectangle(2,4);
		System.out.println("\nA rectangle "+rectangle.toString());
		System.out.println("The area is "+rectangle.getArea());
		System.out.println("The perimeter is "+rectangle.getPerimeter());
	}

}

super关键字:

子类可以继承父类的方法和可访问的数据域,但是不能继承父类中的构造方法,这时就只能用关键字super。

基本语法:1.super() 调用父类的无参构造方法

​ 2.super(argumes) 调用与父类匹配的构造方法

:super()或super(arguments)必须出现在子类构造方法的第一行,这是显式调用父类构造方法的唯一方式

原因:因为在任何情况下,构造一个类的实例时,将会调用沿着继承链的所有父类的构造方法,如果没有被显式调用,编译器会自动地将super()作为构造方法的第一条语句。所以如果没有把显式调用放在第一行,编译器在执行到第一行时会自动将super()加上去,然后再执行到下面时就会出错了

来看一个错误代码

public class Apple extends Fruit{
    
    
    
}

class Fruit{
    
    
    public Fruit(String name){
    
    
        System.out.println(".........");
    }
}

由于Apple没有显式定义构造方法,所以Apple的默认无参构造方法会被隐式调用,即调用Fruit的无参构造方法(因为Apple是Fruit的子类),然而,因为Fruit显式地定义了构造方法,所以Fruit没有无参构造方法,因此,改程序不能被成功编译。

设计指南:一般情况下,最好为每个类提供一个无参构造方法,以便于对该类进行继承,同时避免错误。

方法重写:子类从父类中继承方法,但有时需要修改父类中定义方法的实现

重写与重载的区别:方法重写发生在具有继承关系的不同类中,而方法重载可以发生在同一个类中,也可以发生在具有继承关系的不同类中。

方法重写具有同样的签名,方法重载具有同样的签名但是不同的参数列表

多态

poly + morphism

许多 形态 即多种形态

extends继承或者implements实现,是多态性的前提。

多态意味着父类型可以引用子类型的对象

继承关系使一个子类能继承父类的特征,并且附加一些新特征。子类是它的父类的特殊化,每个子类的实例都是其父类的实例,但是反过来并不成立。例:每个圆都是几何对象,但并非每个几何对象都是圆。因此,总可以将子类的实例传给需要父类型的参数

Eg1:

//父类
public class Father {
    
    
	public void method() {
    
    
		System.out.println("父类方法");
	}
	public void methodf() {
    
    
		System.out.println("父类特有方法");
	}
}
//子类
public class Son extends Father{
    
    
	@Override
	public void method() {
    
    
        //super.method()这样就可以调用父类的method方法,super关键字,Cannot use super in a static context,主函数里面用不了吧
		System.out.println("子类方法");
	}
}
/*
 代码体现多态性的一句话,父类引用指向子类对象
 
 格式:
 父类名称 对象名 = new 子类名称();
 */
public class Demo1 {
    
    
	public static void main(String[] args) {
    
    
		Father obj = new Son();
		obj.method();//输出 子类方法
		obj.methodf();//输出 父类特有方法
	}
}

Eg2:

public class Father {
    
    
	int num=20;
	public void method() {
    
    
		System.out.println(num);
	}
}
public class Son extends Father{
    
    
	int num=10;
    int age=16;
	@Override
	public void method() {
    
    
		System.out.println(num);
	}
}
/*
 访问成员变量的两种方式:
 
 1.直接通过对象名称访问成员变量:等号左边是谁,优先用谁,没有则向上找
 2.间接通过成员方法访问:看该方法属于谁,优先用谁,没有则向上找
 */
public class Demo1 {
    
    
	public static void main(String[] args) {
    
    
		Father obj = new Son();
		System.out.println(obj.num);//输出20
       // System.out.println(obj.age);错误写法,不会向下找的,只会向上找,找objcect,而object里面肯定没有age
        obj.method();//输出10,如果子类没有方法重写的话就输出20
	}
}

Eg3:

public class Father {
    
    
	public void method() {
    
    
		System.out.println("父类方法");
	}
	public void methodf() {
    
    
		System.out.println("父类特有方法");
	}
}
public class Son extends Father{
    
    
	public void method() {
    
    
		System.out.println("子类方法");
	}
	public void methods() {
    
    
		System.out.println("子类特有方法");
	}
}
/*
 在多态的代码中,成员方法的访问规则是:
 看new的是谁,就优先用谁,没有则向上找
  
  编译看左边,运行看右边
 */
public class Demo1 {
    
    
	public static void main(String[] args) {
    
    
		Father obj = new Son();
		obj.method();
		obj.methodf();
		obj.methods();//编译报错,因为父类没有methods这个方法
	}
}

总结:多态:基类型对象访问派生类重写的方法

向上转型

对象的向上转型,其实就是多态写法

父类名称 对象名 = new 子类名称();   //Animal animal = new Cat()  创建了一只猫,把他当作动物
含义:右侧创建一个子类对象,把他当做父类来看待使用
    
==注==:向上转型一定是安全的,从小范围到大范围。但也有一个弊端,就是对象一旦向上转型成父类,那么就无法再调用子类原本特有的内容。(即上面Demo1里面注释编译报错的那一行,因为methods是子类特有的内容),此时就有了向下转型
 类似于:
  double num=100;//正确,int->double 
向下转型

对象的向下转型,其实就是一个【还原】的动作

格式:子类名称 对象名 = (子类名称) 父类对象;(类似于强制类型转换)

含义:将父类对象,还原成本来的子类对象

Animal animal = new Cat();//本来是猫,向上转型为动物

Cat cat = (Cat) animal;//本来是猫,已经被当作动物了,现在还原成原来的猫

注意:a.必须保证对象本来创建的时候,就是猫,才能向下转型成为猫,如果对象原来创建的时候不是猫,现在非要向下转型成为猫,就会报错。(编译不会出错,运行会出错)

那怎么知道父类引用的对象中本来是什么子类呢?

这时就需要使用instanceof

public class Demo2 {
    
    
	public static void main(String[] args) {
    
    
	giveMeAPet(new Dog());
	}
}

public static void giveMeAPet(Animal animal){
    
    
    if(animal instanceof Dog){
    
    
        Dog dog = (Dog) animal;
        dog.watchHourse();
    }
    if(animal instanceof Cat){
    
    
		Cat.cat = (Cat) animal;
        cat.catchHouse();
    }
}
//给我了一只动物,但我只知道她是动物,所以此时要用instanceof判断他是什么,从而物尽其用。
多态的应用

1.循环调用基类对象,访问不同派生类方法

public static void main(String[] args) {
    
    
GradedActivity[] tests = new GradedActivity[3];

    // 第一次考试采用五级计分制,考了75
    tests[0] = new GradedActivity();
    tests[0].setScore(75);

    // 第二次考试采用二级计分制(P或者F)。总共20题,每题分值相同,考生答错5题。
    // 通过的最低分数线是60分
    tests[1] = new PassFailExam(20, 5, 60);
 
    // 第三次是期末考试也采用五级计分制. 总共50题,每题分值相同,考试答错7题
	tests[2] = new FinalExam(50, 7);

    // 显示每次考试的分数和等级
	for(int i=0; i<tests.length; i++){
    
    
   		showValue(tests[i]);
	}
}

	public static void showValue(GradedActivity exam){
    
    
		System.out.println("Score: " + exam.getScore()+ "\t" + 
		"Grade: " + exam.getGrade());
	}

2.实参是派生类,形参是基类

抽象方法

图形类(Geometry)是矩形和圆形等具体形状类的共同父类,他有一个getArea方法的功能是计算图形的面积,但是在图形类中并不知道具体的形状,因此无法计算,也就是说,getArea方法的方法在父类中实际上没有任何意义。

解决办法:通过abstract关键字将getArea方法修饰为抽象的,此时的方法称为抽象方法
抽象方法是出现在基类中的一种方法,但要求在派生类中被重写。(如果派生类没有重写抽象方法,编译器就会报错,抽象方法被用来确保派生类会实现这个方法)

格式:访问修饰符 abstract 返回类型 方法名(参数列表);

例:public abstract void getArea();//关键字abstract 出现在方法头中,方法头以分号结尾

若类含有抽象方法,则类必须以abstract关键字声明为抽象类。
格式: public abstract class 类名

注意

1.不论抽象类是否含抽象方法,其都不允许实例化,即不能创建抽象类的对象,因为其描述的是抽象概念。它只能作为其他类的基类。
2.若父类是抽象类,且子类不想成为抽象类,则子类必须将父类中的所有抽象方法重写为带方法体的普通方法,否则子类仍必须是抽象类。

super的使用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o4EHFcaL-1690521335713)(https://gitee.com/aure0219/typora-img/raw/master/202307281312159.png)]

调用父类的构造方法

抽象类与接口的异同点
不同点:

1.关键字不同:

​ ① 继承抽象类的关键字是extends,而实现接口的关键字是implements;

​ ②定义抽象类的关键字是abstract class,而定义接口的关键字是interface;

2.权限修饰不同:抽象方法可以有public、protected和default这些修饰符(缺省情况下默认为public),而接口方法只能是public ;

3.抽象类中可以有构造方法,而接口中不能有构造方法;

如果写了构造方法,编译报错:Interfaces cannot have constructors

4.抽象类中既可以有抽象方法也可以有普通方法,接口中只能有抽象方法;

:从jdk1.8开始允许接口中出现非抽象方法,但需要使用default关键字修饰。

意义:
1.假如你接口中有很多的抽象方法,但是实现类中有时候并不需要用到那么多的抽象方法,但是又要全部重写,就会很麻烦,这时如果使用非抽象方法,就可以在实现类中重写需要使用到的抽象方法即可,大大减少了代码量。
2.当我们在接口中增加一个抽象方法时,这时所有实现接口的类中都要重写这个方法,就有牵一发而动全身的麻烦。那么如果用非抽象方法,就可以根据实际需要重写该非抽象方法。

5.抽象类中增加方法可以不影响子类,而接口中增加方法通常都影响子类。

理解:因为在抽象类中,可以定义非抽象方法,这时就不需要在子类中重写了,而接口中增加方法,这方法肯定是抽象类方法,则必须要在子类中重写。(为什么是通常呢,因为上面第四点提到,在jdk1.8开始,允许接口中出现非抽象方法)

6.抽象类中的变量可以是普通变量,接口里定义的变量只能是公共的静态的常量;

实际操作中发现定义变量时没写public static final也不会报错,因为接口只能是公共的静态常量的,编译器默认会加上,同理,定义方法时没写public abstract也不会报错

7.抽象方法可以继承单个类和实现多个接口,接口可以多继承接口;

	abstract class Demo{
    
    
		abstract void printfa();//抽象方法
        void printfb();//编译报错,add body
		void printfb() {
    
    
			//非抽象方法
		}
	}
	interface Demo1{
    
    
		void printfab();
		void printfa() {
    
    
		//编译报错,因为接口中必须是抽象方法	
		}
		 default void printfb() {
    
    
		//加上default可以了	
		}
	}

相同点:

(1) 都可以被继承

(2) 都不能被实例化

(3) 都可以包含方法声明

(4) 派生类必须实现未实现的方法

从设计理念层面分析

转:Java中abstract class 和 interface 的解释和他们的异同点(转) - petercao - 博客园 (cnblogs.com)

abstract class在Java语言中体现了一种继承关系,要想使得继承关系合理,父类和派生类之间必须存在"is-a"关系,即父类和派生类在概念本质上应该是相同的。对于interface来说则不然,并不要求interface的实现者和interface定义在概念本质上是一致的,仅仅是实现了interface定义的契约而已。

eg:

假设在我们的问题领域中有一个关于Door的抽象概念,该Door具有执行两个动作open和close,此时我们可以通过abstract class或者interface来定义一个表示该抽象概念的类型。

abstract class Door {
    
    
	abstract void open();
	abstract void close();
}


//或者
interface Door{
    
    
	void open();
	void close();
}

如果现在要求Door还要具有报警的功能。我们该如何设计针对该例子的类结构呢?

方案1:

简单的在Door的定义中增加一个alarm方法

abstract class Door{
    
    
abstract void open();
abstract void close()abstract void alarm();
}


// 或者
interface Door{
    
    
void open();
void close();
void alarm();
}

那么具有报警功能的AlarmDoor的定义方式如下:

class AlarmDoor extends Door{
    
    
void open(){
    
    }
void close(){
    
    }
void alarm(){
    
    }
}


//  或者
class AlarmDoor implements Doorvoid open(){
    
    }
void close(){
    
    }
void alarm(){
    
    }

这种方法违反了面向对象设计中的一个核心原则 ISP (Interface Segregation Principle),在Door的定义中把Door概念本身固有的行为方法和另外一个概念"报警器"的行为方法混在了一起。这样引起的一个问题是那些仅仅依赖于Door这个概念的模块会因为"报警器"这个概念的改变(比如:修改alarm方法的参数)而改变。

什么是ISP?

在软件工程领域,接口隔离原则(ISP)规定不应强迫客户端依赖它不使用的方法。ISP将非常大的接口拆分为更小和更具体的接口,以便客户端只需知道它们感兴趣的方法。这种缩小的接口也称为角色接口。ISP旨在使系统分离,从而更容易重构,更改和重新部署。ISP是面向对象设计的五个SOLID原则之一,类似于GRASP的高内聚原则。

大接口拆分为小接口:就是一个类实现多个接口。

不应强迫客户端依赖它不使用的方法:那就是说几个小接口要合理划分

方案2:

既然open、close和alarm属于两个不同的概念,根据ISP原则应该把它们分别定义在代表这两个概念的抽象类中。定义方式有:这两个概念都使用 abstract class 方式定义;两个概念都使用interface方式定义;一个概念使abstract class 方式定义,另一个概念使用interface方式定义。显然,由于Java语言不支持多重继承,所以两个概念都使用abstract class方式定义是不可行的。后面两种方式都是可行的,但是对于它们的选择却反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理。

如果我们对于问题领域的理解是:AlarmDoor在概念本质上是Door,同时它有具有报 警的功能。我们该如何来设计、实现来明确的反映出我们的意思呢?前面已经说过,abstract class在Java语言中表示一种继承关系,而继承关系在本质上是"is-a"关系。所以对于Door这个概念,我们应该使用 abstract class方式来定义。另外,AlarmDoor又具有报警功能,说 明它又能够完成报警概念中定义的行为,所以报警概念可以通过interface方式定义.

abstract class Door{
    
    
abstract void open();
abstract void close()}
interface Alarm{
    
    
void alarm();
}
class Alarm Door extends Door implements Alarm{
    
    
void open(){
    
    }
void close(){
    
    }
void alarm(){
    
    }
}

总结:abstract class表示的是"is-a"关系,interface表示的是"like-a"关系

集合框架
List

1.ArrayList类

image-20220428123511829

存储在ArrayList中的元素必须是一种对象

package hhhhh;

import java.util.ArrayList;
import java.util.Scanner;

public class EX11_9 {
    
    
	public static void main(String[] args) {
    
    
		ArrayList<Integer> list=new ArrayList<>();//建立一个列表
		Scanner input =new Scanner(System.in);
		System.out.print("Enter integers (input ends with 0): ");
		int value;
		do {
    
    
			value =input.nextInt();
			if(!list.contains(value)&&value!=0)
				list.add(value);//添加元素
		}while(value!=0);
			
			for(int i=0;i<list.size();i++) {
    
    
				System.out.print(list.get(i)+" ");//访问下标是i的元素
			}
        //另一种循环遍历的方式
        /*
		  for(int i:list) { 
		  	System.out.println(i); 
		  }
		 */
        
	}

}

Reward:建立列表的方式以及 多组输入遇到某个值退出的操作

//列表的排序 java.util.Collections.sort(list);

list.sort(null);

//删除元素
list.remove(value);//删除下标为value的,但是如果value是对象的话,就可以删除改对象
 

//判断元素是否存在
System.out.println(list.contains(5));//判断列表中是否有5

//找元素的位置
System.out.println(list.indexOf(3));//找列表中3的位置(从0开始的)

//设置元素
list.set(1, 0);//下标为1的赋值0

//获取元素
list.get(i);//获取下标为i的值


//遍历操作
1、通过索引值访问,可顺序遍历,也可随机访问。
    for(int i=0;i<list.size();++i) {
    
    
		System.out.println(list.get(i));
	}
2.通过ForEach循环访问,实现顺序遍历
	for(Integer v:list) {
    
    
		System.out.println(v);
	}
3.通过迭代器访问,实现顺序遍历。
    Iterator it=list.iterator();
	while(it.hasNext()) {
    
    
		Integer x=(Integer)it.next();
		System.out.println(x);
	}
或者
	Iterator it=list.iterator();
	while(it.hasNext()) {
    
    
		//Integer x=(Integer)it.next();
		System.out.println(it.next());
	}

2.LinkedList类

LinkedList也像ArrayList一样实现了基本的List接口。LinkedList还添加了可以使其用作栈、队列或双端队列的方法。

	LinkedList <Integer> l=new LinkedList<Integer>();
	l.push(1);//元素压入栈中
	l.push(2);
	l.pop();//删除栈顶元素
	System.out.println(l.peek());//取出栈顶元素,输出1(说明这是栈,后进先出)

小结:

ArrayList数组线性表的特点为:类似数组的形式进行存储,因此它的随机访问速度极快。

ArrayList数组线性表的缺点为:不适合于在线性表中间需要频繁进行插入和删除操作。因为每次插入和删除都需要移动数组中的元素。

LinkedList的链式线性表的特点为: 适合于在链表中间需要频繁进行插入和删除操作。

LinkedList的链式线性表的缺点为: 随机访问速度较慢。查找一个元素需要从头开始一个一个的找。

Set

1.HashSet类

	HashSet <Integer>s= new HashSet<>();
	s.add(1);
	s.add(2);
	s.add(4);
	for(Integer x:s) {
    
    
		System.out.println(x);
	}

2.TreeSet类

public class Student implements Comparable{
    
    
	private String number;
	private String name;
	private int age;
	
	Student(String number,String name,int age){
    
    
		this.number=number;
		this.name=name;
		this.age=age;
	}
	
	public String toString() {
    
    
		return "学号:"+number+" 姓名:"+name+" 年龄:"+age;
	}
	public String  getNumber() {
    
    
		return number;
	}
	public String getName() {
    
    
		return name;
	}
	public int getAge() {
    
    
		return age;
	}

	
	@Override
	public int compareTo(Object o) {
    
    
		Student p=(Student) o;
		if(age>p.age) return 1;
		else return -1;
	}
}
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;
import java.util.TreeSet;
public class Demo2 {
    
    
	public static void main(String[] args) {
    
    
		Scanner input = new Scanner(System.in);
		Set<Student> students = new TreeSet<Student>();
		while (true) {
    
    
			String number = input.next();
			if (number.equals("exit"))
				break;// java中判断字符串相等不能用==
			String name = input.next();
			int age = input.nextInt();
			students.add(new Student(number, name, age));
		}

		// 按年龄从小到大输出
		// 迭代器的写法
		Iterator <Student> it=students.iterator();
		while(it.hasNext()) {
    
    
			Student s=(Student)it.next();
			System.out.println(s.toString());
		}
		
		// ForEach的写法
		for (Student student : students) {
    
    
			System.out.println(student.toString());
		}

	}

}

小结:

HashSetTreeSet都有一个共同的特性就是集合的唯一性.TreeSet只是多了一个排序的功能.

TreeSet 是一个有序的集合,它的作用是提供有序的Set集合。TreeSet中的元素支持2种排序方式,自然排序(升序)或者根据创建TreeSet时提供的 Comparator 进行排序。

Map

1.HashMap

		Map<String,Integer> mp=new HashMap<String,Integer>();
		mp.put("ac", 111);//存取数据
		mp.put("bc",12);
		System.out.println(mp.get("ac"));//获取键为ac的值
		System.out.println(mp.containsKey("ac"));//判断是否存在键为“ac”
		System.out.println(mp.containsValue(12));
		//迭代器遍历
		Iterator iter=mp.entrySet().iterator();
		while(iter.hasNext()) {
    
    
			Map.Entry entry=(Map.Entry)iter.next();
			System.out.println(entry.getKey()+" "+entry.getValue());
		}




Map<String,Integer> mp=new HashMap<String,Integer>();
		mp.put("111", 1);
		mp.put("121", 1);
		mp.put("311", 1);
		mp.put("114", 1);
		mp.put("123", 1);
		Iterator iter=mp.entrySet().iterator();
		while(iter.hasNext()) {
    
    
			Map.Entry entry=(Map.Entry)iter.next();
			System.out.println(entry.getKey()+" "+entry.getValue());
        }
//输出
121 1
111 1
123 1
311 1
114 1
//和c++一样,也是随机的,并不是按顺序输出
  1. TreeMap
		Map<String,Integer> mp=new TreeMap<String,Integer>();
		mp.put("bc", 111);
		mp.put("ac",12);
		System.out.println(mp.get("ac"));
		System.out.println(mp.containsKey("ac"));
		System.out.println(mp.containsValue(12));
		
		Iterator iter=mp.entrySet().iterator();
		while(iter.hasNext()) {
    
    
			Map.Entry entry=(Map.Entry)iter.next();
			System.out.println(entry.getKey()+" "+entry.getValue());
		}
输出
12
true
true
ac 12
bc 111
    //可见自然排序是升序

TreeMap存储时会进行排序的,会根据key来对key-value键值对进行排序,其中排序方式也是分为两种,一种是自然排序,一种是定制排序,具体取决于使用的构造方法。

内置API
Object 类

基本特点
Object 是所有类的父类,位于 java.lang包中 数组也是 Object 类的子类
Object 类的常用方法
toString()可以将任何一个对象转换成字符串返回,返回值的生成算法为:
getClass().getName() + '@' + Integer.toHexString(hashCode())

equals() 用来比较两个引用的虚地址。当且仅当两个引用在物理上是同一个对象时,返回值为true,否则将返回false。
hashCode() 获取对象的哈希码值,为16进制
Object类在 java.lang 包下,是所有类的根。任何类的对象,都可以调用 Object类中的方法,包括数组对象

包装器类型 (Wrapper类)

Java 提供一系列包装类,以便将基础数据类型当做对象进行操作
在 java.lang 包中,对于每个基本数据类型都有一个对应的包装类

基本数据类型 包装类
boolean Boolean
byte Byte
char Character
short Short
int Integer
long Long
float Float
double Double

1.xxxx.valueOf() 该方法会把字符数字转换成对应的数据类型 (该方法是一个重载方法,可以把 int 数据转换为 Integer 对象,也可以把字符类型数字转换成 Integer 对象)
axxx 可以为 Integer
bxxx 可以为 Short
cxxx 可以为 Byte
dxxx 可以为 Long 等等
2.除了 Boolean 和 Character类以外,其他的包装类都有静态的 parseXxx 方法 (Xxx 代表数据类型)用于将字符串转换成对应的基本数据类型的值

		String s2 = "123";
		int a = Integer.valueOf(s2);
		System.out.println(123 == a);//true

		int i = Integer.parseInt("18"); // 转换成整数
字符串

java.lang 包

创建方式

public class Test {
    
    
 	public static void main(String[] args) {
    
    
        	// 直接创建
    		String str = "Hello World";
        
        	// 使用构造方法
        	String str1 = new String("Hello World"); 
        	//String 是一个类,这就能解释为什么 String 不属于基本类型
        
        	/**
            * 两者区别:
            * 使用构造方法每次创建的都是一个 String 对象
            * 使用直接赋值的方式创建两个相同的对象,两者虚地址是一样的,实际上代表是相同的字符串
            */
    }
}

常见方法

长度 s.length()
去空格 s.trim()
右对齐 String.format(“%10s”,s)
左对齐 String.format(“%-10s”,s)
时间转字符串
包含查询 s.contains(" ");
下标查询 s.indexOf(" ");
判断是否为空 if(s==null||s.isEmpty())
字符串比较 equals
		String str="你好啊";
		String str1="12";
		String str2="123";
		System.out.println(str.contains("好啊"));//true
		System.out.println(str.contains("你好啊啊"));//false
		System.out.println(str.indexOf("好"));//1
		System.out.println(str.indexOf("你好"));//0
		System.out.println(str.indexOf("你"));//0
		System.out.println(str.indexOf("1"));//-1  找不到输出-1
		System.out.println(str1.equals(str2));//false
		System.out.println(str1.equals("12"));//true
		
		//时间转字符串
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		String time = simpleDateFormat.format(date);
		System.out.println(time);
		

下面来说明为什么在java中字符串比较要用equals

public class Main {
    
    
 	public static void main(String[] args) {
    
    
        	// 直接创建
    		String s1 = "Hello World";
        	String s2 = "Hello World";
        	System.out.println(s1 == s2); // 返回 true , 因为使用等号赋值,比较的是值
        	// 使用构造方法
        	String str1 = new String("Hello World"); 
        	String str2 = new String("Hello World");
        	System.out.println(str1 == str2); // 返回 false  虚地址不一样
			System.out.println(str1.equals(str2)); // 返回 true,这时候比较的就是值了
        	// 所以一般使用 equals 方法比较字符串的值很常用 (equalsIgnoreCase 是忽略大小写比较)	
    }
}

为什么字符串比较不使用==, 因为比较的是 虚‘地址,equalsIgnoreCase() 会忽略大小写比较字符串

文件
File类
//	java.io.file
		/*
	 *我们可以使用File类的方法
	 *创建一个文件/文件夹 
	 *删除文件/文件夹
	 *获取文件/文件夹
	 *判断文件/文件夹是否存在
	 *对文件进行遍历
	 *获取文件的大小
	 *File类是一个与操作系统无关的类,任何的操作系统都可以使用这个类中的方法
	 */
/*
 * 绝对路径:是一个完整的路径
 * 	以盘符开始的路径
 * 	C:\\demo\\b.txt
 * 相对路径:是一个简化的路径
 * 	相对指的是相对与当前项目的根目录
 * 	如果使用当前项目的根目录,路径可以简化书写
 * 	例 C:\\users\\ideaprojects\\homework\\123.txt  可以简化为123.txt
 * 注意:1.路径是不区分大小写的
 * 		 2.路径中的文件名称分隔符在windows中使用反斜杠,反斜杠是转义字符,两个反斜杠等于一个反斜杠
 */

​ 文件名称分隔符 Windows:反斜杠\ Linux:正斜杠/

​ 路径分隔符 Windows:分号; Linux:冒号:

两个获取路径的方法:

public String getPath();
public String getAbsolutePath();//无论路径是绝对的还是相对的,返回的都是绝对路径

/*
	 * File(String pathname) 通过将给定路径名字符串转换为抽象路径名来创建一个新File实例
	 * String pathname:字符串的路径名称
	 * 路径可以是以文件结尾,也可以是以文件夹结尾
	 * 路径可以是绝对路径,也可以是相对路径
	 * 路径可以是存在的,也可以是不存在的
	 * 创建File对象,只是把字符串路径封装为File对象,不考虑路径的真假情况
	 */
File f1=new File("C:\\Users\\14716\\Desktop\\leetcode\\12.txt");
	System.out.println(f1);
/*
输出
C:\Users\14716\Desktop\leetcode\12.txt
说明是重写了Object类的toString方法,打印的是路径

File类的一些判断功能

public boolean exists();//此File表示的文件或目录是否实际存在

public boolean isDirectory();//用于判断构造方法中给定的路径是否以文件夹(目录)结尾

public boolean isFile();//用于判断构造方法中给定的路径是否以文件结尾

==注:==如果路径不存在,返回false

Stream流
  • 数据流又分为输入流和输出流

  • 输入流:往内存中读叫输入流

  • 输出流:从内存中往外些叫输出流

  • 所有输入流都是 InputStream 类或者 Reader 类的子类
    类名以 inputStream 结尾的类都是 InputStream 的子类
    类名以 Reader 结尾的类都是 Reader类的子类
    所有输出流都是 OutputStream 类 或者 Writer类的子类
    类名以 OutputStrean结尾的类都是 OutputStream的子类
    类名以 Writer结尾的类都是 Writer 类的子类

  • 输入输出流又分为字节流字符流

    1.字节流:以字节为基本单位 , 在 java.io包中,大部分操作继承InputStream(输入字节流)类和OutputStream(输出字节流)类

    2.字符流:两个字节为基本单位,专门处理字符串和文本,对于字符流进行操作的类主要是Reader(读取流)类和 Writer(写入流)类。

接下来是常用的三种读写文件的方式

1.FileWriter和FileReader

对文件内容按字符读取

String dir = "E:\\soft\\aaa\\a.txt";
File file = new File(dir);
//如果文件不存在,创建文件
if (!file.exists()) 
    file.createNewFile();
//创建FileWriter对象
FileWriter writer = new FileWriter(file);
//向文件中写入内容
writer.write("the first way to write and read");
writer.flush();
writer.close();

//创建FileReader对象,读取文件中的内容
FileReader reader = new FileReader(file);
char[] ch = new char[100];
reader.read(ch);
for(char c:ch) {
    
    
    System.out.print(c);
}
System.out.println();
reader.close();
2.BuffredReader和BufferedWriter

对文件内容进行整行读取

String dir = "E:\\soft\\aaa\\b.txt";
File file = new File(dir);
//如果文件不存在,创建文件
if (!file.exists()) 
    file.createNewFile();
//创建BufferedWriter对象并向文件写入内容
BufferedWriter bw = new BufferedWriter(new FileWriter(file));
//向文件中写入内容
bw.write("the second way to write and read");
bw.flush();
bw.close();
//创建BufferedReader读取文件内容
BufferedReader br = new BufferedReader(new FileReader(file));
String line;
while ((line=br.readLine())!=null) {
    
    
    System.out.println(line);
}
br.close();
3.FileInputStream和FileOutputStream

以字节的形式写入文件,读取文件时先读取字节数组,再将字节数组转换为字符串形式

注意由于字节流的缘故,因此无法读取中文字符

String dir = "E:\\soft\\aaa\\c.txt";
File file = new File(dir);
//如果文件不存在,创建文件
if (!file.exists()) 
    file.createNewFile();
//创建FileOutputStream对象,写入内容
FileOutputStream fos = new FileOutputStream(file);
//向文件中写入内容
fos.write("the third way to write and read".getBytes());
fos.close();
//创建FileInputStream对象,读取文件内容
FileInputStream fis = new FileInputStream(file);
byte[] bys = new byte[100];
while (fis.read(bys, 0, bys.length)!=-1) {
    
    
    //将字节数组转换为字符串
    System.out.println(new String(bys));
}
fis.close();

猜你喜欢

转载自blog.csdn.net/Aure219/article/details/131978099
今日推荐