自学javase的回顾(3/5)
回顾第三天,持续更新…
1、类方法扩展和数组
类方法扩展:equals方法
当需要进行判断两个对象之间属性之间是否相等的时候,如果用equals进行判断,就会出现内存地址的比较。
public class Test {
public static void main(String[] args) {
Person p1=new Person("张三");
Person p2=new Person("张三");
System.out.println(p1.equals(p2));//false
}
}
class Person{
String name;
public Person(String name) {
this.name = name;
}
}
在现实当中,false结果是不合理的,所以正常情况下,需要重写equals方法。
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(name, person.name);
}
当两个内存地址相同的时候,返回true;如果比较的这个对象为null或者对象o不属于这个类,则返回false。将对象o转换为Person类的对象,然后比较属性。这里比较字符串也是调用了String类写好的equals方法。因为字符串比较不能使用双等号,字符串的创建也是对象的创建。
大结论:凡是基本数据类型的比较可以使用双等号进行比较。引用数据类型不能使用双等号进行比较,要使用equals方法进行比较。
toString方法:
凡是System.out.println(“字符串”),实际上都是System.out.println(“字符串”.toString()),而字符串对象在类体里面写好了toString方法,所以在终端上看到的是字符串的输出,如果输出一个对象,而这个对象没有重写toString方法,就会出现在终端的是类名@对象的内存地址:
public class Test {
public static void main(String[] args) {
Person p1=new Person("张三");
System.out.println(p1);//Person@2d98a335
}
}
class Person{
String name;
public Person(String name) {
this.name = name;
}
}
上述结果并不符合正常逻辑,所以一般情况下,需要重写toString方法:
@Override
public String toString() {
return name;
}
finalize方法:
这个方法是超类Object中的方法,它的源代码是protected void finalize() throws Throwable{},它是垃圾回收机制的方法,如果想要在垃圾回收机制回收对象的时候执行什么语句,就可以在重写这个方法,加上想要执行的代码块。
public class Test {
public static void main(String[] args) {
for(long i=0;i<100000000;i++){
Person p=new Person();
p=null;
}
}
}
class Person{
String name;
@Override
protected void finalize() throws Throwable {
System.out.println("对象已销毁");
}
}
hasCode方法:
它调用了底层去将对象的内存地址经过哈希算法转换成数值的方法。
public class Test {
public static void main(String[] args) {
Person p=new Person();
System.out.println(p.hashCode());//764977973
}
}
class Person{
String name;
}
匿名内部类:
什么是内部类?
类方法中定义了一个新的类,这个类被称为内部类。例如在类中定义接口的新类。
什么是匿名内部类?
局部内部类的一种,直接new 接口的方法去定义接口的方法
public class Test {
public static void main(String[] args) {
Sum s=new Sum();
int c=s.add(new Mymath() {
@Override
public int sum(int a,int b) {
return a+b;
}
},100,200);
System.out.println(c);//300
}
}
interface Mymath{
int sum(int a,int b);
}
class Sum{
public int add(Mymath my,int a,int b){
int c=my.sum(a,b);
return c;
}
}
数组:
数组是方便程序员储存数据的空间。
数组有以下特点:
1、数组只能存储一种数据类型的数据
2、数组中尽量不要存储大数据量的数据
3、一旦创建了数组,数组的长度不可变
4、数组存储的对象内存地址是连续的,因为连续和每个元素所占空间大小一致,通过数学计算之后,检索效率高。又因为内存地址连续,所以删除或者增加某个元素都会发生较大的变动,增删的效率较低。但是删除和增加最后一个元素没影响。
5、数组也是一个对象,它的内存地址是第一个元素的内存地址
如何初始化一个一维数组?
静态初始化语法:类型[] 数组名={元素,元素};
动态初始化语法:类型[] 数组名=new 类型[元素的个数];(它的元素的值都为null,需要自己手动赋值)
如何初始化一个二维数组?
什么是二维数组?
一个数组中的元素都是一维数组,这个数组被称为二维数组。
静态初始化语法:类型[][] 数组名={{元素,元素},{元素,元素}};
动态初始化语法:类型[][] 数组名=new 类型[元素的个数][一维数组元素的个数];
public class Test {
public static void main(String[] args) {
int[] i={7,4,8};
int[] i1=new int[3];
int[][] i2={i,i1};
int[][] i3=new int[2][2];
}
}
如何去遍历这些数组?
这里会有两种遍历数组的方式:
1、for循环
public class Test {
public static void main(String[] args) {
int[] i={7,4,8};
int[] i1=new int[3];
int[][] i2={i,i1};
int[][] i3=new int[2][2];
print(i);// 7 4 8
System.out.println();
print(i1);// 0 0 0
System.out.println();
print(i2);// 7 4 8 0 0 0
System.out.println();
print(i3);// 0 0 0 0
}
public static void print(int[] i){
for(int c=0;c<i.length;c++){
System.out.print(" "+i[c]);
}
}
public static void print(int[][] i){
for(int c=0;c<i.length;c++){
for(int c1=0;c1<i[c].length;c1++){
System.out.print(" "+i[c][c1]);
}
}
}
}
2、foreach循环
什么是foreach循环,这是一种更加便利的循环工具,循环出数组或者集合中的每一个元素
语法:for(元素的类型 遍历的变量名:数组或者集合名){
System.out.println(遍历的变量名);
}
public class Test {
public static void main(String[] args) {
int[] i={7,4,8};
int[] i1=new int[3];
int[][] i2={i,i1};
int[][] i3=new int[2][2];
for(int i4:i){
System.out.println(i4);// 7 4 8
}
for(int[] i5:i2){
for(int i6:i5){
System.out.println(i6);// 7 4 8 0 0 0
}
}
}
}
数组扩容:
由于数组创建之后不可改变长度的特短,所以扩容就是创建一个更大的新数组,将旧数组传入新数组的过程叫做数组扩容。数组扩容是System的方法。
语法:System.arraycopy(旧数组名,拷贝旧数组的下标,新数组名,拷贝到新数组的下标,元素的个数);
public class Test {
public static void main(String[] args) {
int[] i = {7, 4, 8};
int[] i2=new int[5];
System.arraycopy(i,0,i2,0,3);
for(int i3:i2){
System.out.println(i3);//7 4 8 0 0
}
}
}
数组增删:
由于数组长度不可变的特点,和数组扩容采用同一原理进行增删。也可以将数组转换为集合,再调用集合的remove方法。
2、排序方法和查找方法
数组的排序有sort方法,sort方法是Arrays的方法。
public class Test {
public static void main(String[] args) {
int[] i = {7, 4, 8};
Arrays.sort(i);
for (int i5 : i) {
System.out.println(i5);// 4 7 8
}
}
}
数据的两种常见排序方法:
1、冒泡排序法
int[] i={5,2,4,6,1};
冒泡排序这里是采用了两两元素根据大小然后相互对调的方法。
每一次循环都要找出最大的元素放到最右边。然后取出最右边的元素
第一次循环:2,4,5,1,6
第二次排序:2,4,1,5
第三次循环:2,1,4
第四次循环:1,2
第五次循环:1
倒着取出所有最右边的元素:1,2,4,5,6
冒泡排序的规律:元素的个数为n,总共需要循环n次,每次循环需要对调n-1次,对比也是n-1次。
public class Test {
public static void main(String[] args) {
int[] i = {5,2,4,6,1};
for(int i1=i.length;i1>0;i1--){
for(int i2=0;i2<i1-1;i2++){
if(i[i2]>i[i2+1]){
int y=i[i2];
i[i2]=i[i2+1];
i[i2+1]=y;
}
}
}
for(int i3:i){
System.out.println(i3);//1 2 4 5 6
}
}
}
2、选择排序法
int[] i={5,2,4,6,1};
选择排序是每次循环找出最小的元素放到最左边
第一次循环:1,2,4,6,5
第二次循环:2,4,6,5
第三次循环:4,6,5
第四次循环:5,6
第五次循环:6
每次循环取出最右边的元素:1,2,4,5,6
选择排序的规律:元素的个数为n,总共需要循环n次,每次循环对调1次,对比n-1次。
public class Test {
public static void main(String[] args) {
int[] i = {5,2,4,6,1};
for(int i1=0;i1<i.length;i1++){
int min=i1;
for(int i2=i1+1;i2<i.length;i2++){
if(i[i1]>i[i2]){
min=i2;
}
}
if(min!=i1){
int y=i[min];
i[min]=i[i1];
i[i1]=y;
}
}
for(int i3:i){
System.out.println(i3);//1 2 4 5 6
}
}
}
数据查找一般来说有两种查找方法:
1、挨个查找
int[] i={5,2,4,6,1};
例如想找元素为4的下标,挨个查找就是这样的。
public class Test {
public static void main(String[] args) {
int[] i = {5, 2, 4, 6, 1};
for (int i1=0;i1<i.length;i1++){
if(i[i1]==6){
System.out.println(i1);//3
}
}
}
}
2、二分法查找
int[] i={1,2,4,5,6};
二分法查找是对半查找,这个查找方法是基于顺序从小到大排好的查找方法。先记下第一个元素的下标和最后一个元素的下标,这两个下标加和除以二,得到的这个数字就是数组的中间的那个元素的下标,寻找的元素就是从中间开始找,如果查找元素大就往右边查找,然后再次从右边的中间那个元素开始找…例如想找元素为6的下标:
第一次寻找:4,5,6
第二次寻找:5,6
第三次寻找:6
public class Test {
public static void main(String[] args) {
int[] i={1,2,4,5,6};
int index = binarySearch(i,6);
System.out.println(index);//4
}
public static int binarySearch(int[] i1,int i){
int begin=0;
int over=i1.length-1;
int mid=(begin+over)/2;
while (begin<=over){
if(i==i1[mid]){
return mid;
}else if(i>i1[mid]){
begin=mid+1;
mid=(begin+over)/2;
}else if(i<i1[mid]){
over=mid-1;
mid=(begin+over)/2;
}
}
return -1;
}
}
Array工具:
1、sort工具:进行数组排序
2、binary工具:进行数组查找(基于排序好的数组),查找的那个元素如果不存在则返回-1。
public class Test {
public static void main(String[] args) {
int[] i3={5,2,4,6,1};
Arrays.sort(i3);
int index =Arrays.binarySearch(i3,6);
System.out.println(index == -1 ? "该元素不存在" : "该元素下标是" + index);//4
}
}
综上所述,选择排序的对调次数比冒泡排序次数少,所以选择排序的效率更高一些。
3、字符串对象方法和包装类
java.lang.String
在java中使用双引号括起来的都是String对象,字符串一旦创建就无法被改变,因为被final修饰了,双引号的字符串和其他对象不同,它的创建存储在方法区的字符串常量池中。
String i=“abc”;这里的i是指向字符串常量池中的abc的内存地址。
String的构造方法:参数可以是字符串对象、char[]、byte[]等
public class Test {
public static void main(String[] args) {
char[] c={'a','b','c'};
String i=new String(c,1,2);
System.out.println(i);//bc
}
}
下面是字符串常见的方法
public class Test {
public static void main(String[] args) {
char c="中国人".charAt(1);//索引下标的字符
System.out.println(c);
int c1="abv".compareTo("abc");
System.out.println(c1);//对比两个字符串
System.out.println("Hello".contains("He"));
//判断字符串是否存在包含关系,返回值为boolean
System.out.println("Hello".endsWith("lo"));
//判断字符串是否是以后面的字符串为结尾的,返回值为boolean
System.out.println("Hello".equals("Hello"));
//判断字符串是否相等
System.out.println("Hello".equalsIgnoreCase("hello"));
//判断字符串是否相等,并且忽略大小写
byte[] c2="acv".getBytes();
//将字符串返回成byte数组
for(int i=0;i<c2.length;i++){
System.out.println(c2[i]);
}
System.out.println("Hello".indexOf("lo"));
//子字符串在此字符串中第一次出现的索引
String c4="";
System.out.println(c4.isEmpty());
//判断字符串是否为空串
System.out.println("abc".length());
//字符串的长度
System.out.println("llolloll".lastIndexOf("ll"));
//最后出现的索引
System.out.println("Hello".replace("l","o"));
//替代字符串
String[] str="1990-10-1".split("-");
//以指定的字符为分隔符来进行分割,以数组的形式接受
for(int i=0;i<str.length;i++){
System.out.println(str[i]);
}
System.out.println("Hello".startsWith("He"));
//判断是否以某个字符串为开始
System.out.println("Hello".substring(3));
//以下标元素开始输出字符串
char[] chars="Hello".toCharArray();
//将字符串转换成char数组
for(int i=0;i<chars.length;i++){
System.out.println(chars[i]);
}
System.out.println("HELLO".toLowerCase());
//将字符串中的大写转换成小写
System.out.println("hello".toUpperCase());
//将字符串中的小写转换成大写
System.out.println(" da".trim());
//去空白的
String i=String.valueOf(10);
//转换字符串
System.out.println(i);
}
}
StringBuffer和Stringbuilder
都是字符串拼接的工具,为什么不使用+来拼接,因为字符串的字符是不可变的,每次创建String对象都要消耗大量内存,效率低,而这两个就是在字符串常量池中创建byte[]数组,是可变的,效率高。它的初始化容量是16为了效率,最好预先给一个合适的容量。
StringBuffer是线程安全的
StringBuilder是非线程安全的
public class Test {
public static void main(String[] args) {
StringBuffer s=new StringBuffer(100);
s.append("i");
s.append(97);
System.out.println(s);//i97
}
}
包装类:
java中为8种数据类型又对应的准备了8种包装类型。8种包装类属于引用数据类型。
为啥要提供8中包装类呢?
有时候方法中的参数想要一个类的对象,而基本数据类型不符合
所以要准备8种包装类
基本数据类型 包装类
byte java.lang.Byte(父类Number)
short java.lang.Short(父类Number)
int java.lang.Integer(父类Number)
long java.lang.Long(父类Number)
float java.lang.Float(父类Number)
double java.lang.Double(父类Number)
boolean java.lang.Boolean(父类Object)
char java.lang.Character(父类Object)
其中有6种的父类都是Number类,Number是一个抽象类
自从jdk1.5之后就有了自动装箱和自动拆箱。
自动装箱就是指将基本数据类型转换成包装类,拆箱与之相反
Interger有几个常用的方法:
1、toBinaryString()、toHexString()、toOctalString()转换进制
2、parseInt()将类型转换成int
注意:Integer这个对象如果调取[-128~127]之间的整数,就不需要通过new对象,引用直接去引用方法区的整数型常量池,而不在这个范围,就需要去在堆中new对象
public class Test {
public static void main(String[] args) {
Integer i=900;
Integer i2=900;
System.out.println(i==i2);//false
Integer i3=127;
Integer i4=127;
System.out.println(i3==i4);//true
}
}
4、日期和数据
BigDecimal:
java.math.BigDecimal
这个处理大数据使用的数学工具,精度极高,适用在银行等场景。
public class Test {
public static void main(String[] args) {
BigDecimal b1=new BigDecimal(22.834234234);
BigDecimal b2=new BigDecimal(234.4564523);
BigDecimal b3=b1.add(b2);
System.out.println(b3);//257.29068653399999533348818658851087093353271484375
}
}
import java.text.DecimalFormat
这个是处理数字的格式化工具
public class Test {
public static void main(String[] args) {
DecimalFormat d=new DecimalFormat("####,###.##");
String i=d.format(12323441.31);
System.out.println(i);//12,323,441.31
}
}
java.util.Random:
这个是关于随机数的工具
public class Test {
public static void main(String[] args) {
Random r=new Random();
int i=r.nextInt(6);
System.out.println(i);//从0-5的随机整数,不包括6
}
}
Date:
java.util.Date
这个是处理日期的工具。
java.text.SimpleDateFormat
这个是对日期格式的处理工具。
public class Test {
public static void main(String[] args) {
Date date=new Date();
System.out.println(date);//Fri Jul 03 17:59:18 CST 2020
SimpleDateFormat s=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:sss");
String date1=s.format(date);
System.out.println(date1);//2020-07-03 17:59:18:005
}
}
有了时间转换为字符串,那么字符串如何转换为时间呢?
parse方法,是SimpleDateFormat对象的方法。
public class Test {
public static void main(String[] args) throws ParseException {
SimpleDateFormat s=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:sss");
Date date=s.parse("2000-12-20 24:23:34:340");
String s1=s.format(date);
System.out.println(s1);//2000-12-21 00:28:40:040
}
}
System.currentTimeMillis方法,返回值是long类型。这个方法是计算出从1970年1月1日到现在的时间总和,单位是毫秒。这个方法可以计算出程序执行的时间。在程序开始的时候减去结束的毫秒数,就是程序执行的时间。
public class Test {
public static void main(String[] args) {
long begin=System.currentTimeMillis();
for(int i=0;i<100;i++){
System.out.println(i);
}
long over=System.currentTimeMillis();
System.out.println(over-begin);//2
}
}
5、枚举和异常
枚举:
什么是枚举?
字面意思来说就是列出所有成员的意思。在java中是指定返回值数目和具体内容。关键字:enum
语法:enum 枚举名{}
public class Test {
public static void main(String[] args) throws ParseException {
System.out.println(grade(70));//优秀
}
public static 状态 grade(int mark){
if(mark<60){
return 状态.差;
}
else if(mark>60&&mark<70){
return 状态.一般;
}
else {
return 状态.优秀;
}
}
}
enum 状态{
差,一般,优秀;
}
异常:
什么是异常?
一般来说,系统在处理代码过程有三种结果:正确编译通过、代码和环境错误(Error)、逻辑错误(Execption)。
代码和环境错误就是语法错误和运行环境不正确,而逻辑错误就是属于逻辑层面的,例如空指针异常、数组下标越界。这些逻辑错误就是异常,是可以通过程序改写来解决的。
异常分为两种异常:
1、编译时异常(ExecptionSubClass以及它的子类):
编译时异常并不是编译时出现的异常,是程序员在编译前必须对这种异常做预先处理,如果没有做处理,就会报错。
2、运行时异常(RuntimeExecption以及它的子类)
运行时异常,是在运行阶段出现的异常,程序员可以对其做预处理,也可以不做。
错误的改正过程就是修改代码逻辑不正确或者语法不对的地方等等。
异常改正的过程为两种:
1、throws抛出异常(交给上级去处理,属于推卸责任,如果上级没办法解决就会报错)
2、try…catch捕捉异常(自行处理,尝试…抓住)
一般情况下,不建议main方法去throws异常,因为JVM处理不了的话,就只能总结程序。
try语句块中处理,如果出现异常,就不执行try语句块,去执行catch语句块。
跟随try…catch语句块的还有一个语句块是finally,这个语句块是最后执行的,在IO流中文件的流要关闭的情况下,就会用到finally语句块。
异常也可以程序员自己定义:
public class Test {
public static void main(String[] args) {
String s="asdf";
MyException exception=new MyException("长度不正确");
if(s.length()!=3){
exception.printStackTrace();
}else{
System.out.println("长度正确");
}
}
}
class MyException extends Exception{
public MyException() {
}
public MyException(String message) {
super(message);
}
}