GitHub:Aure219/JAVA-homework: JAVA-homework (github.com)
1选择Windows Preferences,选择Configuration,点击Add Entry
添加自己的name 和 email
然后生成公钥私钥
复制一下
接下来打开GitHub
点击Settings
然后如图操作
输入title和key
key就是之前复制的公钥
远程仓库new一个
取名
复制SSH 等一下要用到
1.先新建一个Project
2.然后new一个class
取个名字,然后最好勾上圈出来的
接下来写代码
提交到本地仓库
然后再commit
点击你要上传的,通常后缀是.java
再点击2处的绿的加号
然后再3处随便写点东西
最后点最底部的push
这时把刚才拷贝的粘贴到这里来
接下来就一直点,
出现下面这个页面应该就是成功了
然后回到GitHub 发现上面出现了你的代码即可
盘符切换: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.加法的使用
加法的使用方法,是起到连接字符串的作用还是运算的作用
注意最上面加了一行代码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 Door{
void 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"关系
1.ArrayList类
存储在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
的链式线性表的缺点为: 随机访问速度较慢。查找一个元素需要从头开始一个一个的找。
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());
}
}
}
小结:
HashSet
和TreeSet
都有一个共同的特性就是集合的唯一性.TreeSet
只是多了一个排序的功能.
TreeSet
是一个有序的集合,它的作用是提供有序的Set集合。TreeSet
中的元素支持2种排序方式,自然排序(升序)或者根据创建TreeSet
时提供的 Comparator 进行排序。
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++一样,也是随机的,并不是按顺序输出
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键值对进行排序,其中排序方式也是分为两种,一种是自然排序,一种是定制排序,具体取决于使用的构造方法。
基本特点
Object 是所有类的父类,位于 java.lang包中 数组也是 Object 类的子类
Object 类的常用方法
toString()
可以将任何一个对象转换成字符串返回,返回值的生成算法为:
getClass().getName() + '@' + Integer.toHexString(hashCode())
equals()
用来比较两个引用的虚地址。当且仅当两个引用在物理上是同一个对象时,返回值为true,否则将返回false。
hashCode()
获取对象的哈希码值,为16进制
Object类在 java.lang 包下,是所有类的根。任何类的对象,都可以调用 Object类中的方法,包括数组对象
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() 会忽略大小写比较字符串
// 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
数据流又分为输入流和输出流
输入流:往内存中读叫输入流
输出流:从内存中往外些叫输出流
所有输入流都是 InputStream 类或者 Reader 类的子类
类名以 inputStream 结尾的类都是 InputStream 的子类
类名以 Reader 结尾的类都是 Reader类的子类
所有输出流都是 OutputStream 类 或者 Writer类的子类
类名以 OutputStrean结尾的类都是 OutputStream的子类
类名以 Writer结尾的类都是 Writer 类的子类
输入输出流又分为字节流和字符流
1.字节流:以字节为基本单位 , 在 java.io包中,大部分操作继承InputStream(输入字节流)类和OutputStream(输出字节流)类
2.字符流:两个字节为基本单位,专门处理字符串和文本,对于字符流进行操作的类主要是Reader(读取流)类和 Writer(写入流)类。
接下来是常用的三种读写文件的方式
对文件内容按字符读取
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();
对文件内容进行整行读取
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();
以字节的形式写入文件,读取文件时先读取字节数组,再将字节数组转换为字符串形式
注意由于字节流的缘故,因此无法读取中文字符
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();