java-面向对象基础语法


作为一名初次接触java的编程小白,也是初次学习总结,本文内容仅供参考
本文仅供个人学习记录,欢迎交流

”又过去了一个礼拜,这周的学习内容不多不少,但是因为自己没有及时整理,思维还是有些混乱,果然学习最重要的不是看进度,而是及时复盘,一个萝卜一个坑“

chapter 2

1.多态

多态主要是用于适用于在继承的时候,在书写代码的时候,根据具体子类再操作,这么说可能有些抽象不如看下这段代码

class Point {
    private int x;
    private int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public void setX(int x) {
        this.x = x;
    }

    public void setY(int y) {
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public String toString() {
        return String.format("(%d, %d)", x, y);
    }
}
class ColorPoint extends Point {
    private String color;

    public ColorPoint(int x, int y, String color) {
        super(x, y);//补充说明一下super方法,调用父类的属性和方法,this关键字代指的是子类,由此我们也可以分析出为什么java的只支持单继承,因为在使用super关键字的时候,如果有多个父类就难以区别调用的到底是哪个父类,(但支持多接口,这里忍不住想多嘴几句关于多接口的事项:为什么我们说接口总是说实现呢,因为可以理解为,继承其实是一种团队合作的规范,在我们调用后要求实现接口中的所有方法,因此,利用接口,可以规范化我们在团队协作的书写,避免疏漏方法行为)
        this.color = color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public String toString() {
        return String.format("(%d, %d, %s)", super.getX(), super.getY(), this.color);
    }
}

public class Main {
    public static void main(String[] args) {
        Point point = new Point(3, 4);
        Point colorPoint = new ColorPoint(1, 2, "red");
        // 同一个类的实例,调用相同的函数,运行结果不同
        System.out.println(point.toString());
        System.out.println(colorPoint.toString());
    }
}

2.final关键字

final关键字不可被修改,一般用于修饰常量,方法,类在添加final关键字后也不可修改

final int N = 110;//通常被修饰的常量我们使用全部大写的书写格式
   public static void main(String[] args) {
        String a = "hello1";
        final String b = "hello";
        String d = "hello";
        String c = b + 1;
        String e = d + 1;
        System.out.println(a == c);
        System.out.println(a == e);
    }

输出结果:

true
false

Process finished with exit code 0

变量 a 指的是字符串常量池中的hello1;
变量 b 是 final 修饰的,变量 b 的值在编译时候就已经确定了它的确定值,换句话说就是提前知道了变量 b 的内容到底是个啥,相当于一个编译期常量;
变量 c 是 b + 1 得到的,由于 b 是一个常量,所以在使用 b 的时候直接相当于使用 b 的原始值hello来进行计算,所以 c 生成的也是一个常量,a 是常量,c 也是常量,都是 hello1 ,而 Java 中常量池中只生成唯一的一个 hello1 字符串,所以 a 和 c 是相等的;
d 是指向常量池中 hello,但由于 d 不是 final 修饰,也就是说在使用 d 的时候不会提前知道 d 的值是什么,所以在计算 e 的时候就不一样了,e的话由于使用的是 d 的引用计算,变量d的访问却需要在运行时通过链接来进行,所以这种计算会在堆上生成 hello1 ,所以最终 e 指向的是堆上的 hello1 , 所以 a 和 e 不相等。

我们再来看一个类的情况:

很明显在final修饰后,我们无法对对象的属性进行修改了,同理对象也是,我们不能进行修改,具体原因如下:

使用 final 方法原因有两个:

第一个原因是把方法锁定,以防止任何继承类修改它的含义,这是出于设计的考虑:想要确保在继承中使方法的行为保持不变,并且不会被覆盖。
第二个原因是效率,在 Java 的早期实现中,如果将一个方法声明为 final,就是同意编译器将针对该方法的所有调用都转为内嵌调用,内嵌调用能够提高方法调用效率,但是如果方法很大,内嵌调用不会提高性能。而在目前的Java版本中(JDK1.5以后),虚拟机可以自动进行优化了,而不需要使用 final 方法。
关于方法的final使用这里不多赘述了,有兴趣的同学可以参考这篇文章,上述对于final关键字的解析也是搬运自此链接链接:https://juejin.cn/post/6987560852031291399

3.抽象类

*抽象类:

abstract 修饰的类称作为抽象类。

abstract修饰的方法称作为抽象方法。

抽象类要注意的事项:
1. 抽象方法是没有方法体的。
2.抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。
3.类该有的成员(成员变量、方法、构造器)抽象类都可以有。
4.抽象类最主要的特点:抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现。
5.一个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。

public abstract class A {  //抽象类

    //成员变量
    private String name;

    //构造器
    public A(String name) {
        this.name = name;
    }

    //抽象方法
    public abstract void sayHello();


    //普通的方法
    public void print(){
        System.out.println("我是非抽象的方法");
    }
}

public class B  extends A{


    public B(String name) {
        super(name);
    }

    @Override
    public void sayHello() {
        System.out.println("早上好!");
    }
}

public class Test {

    public static void main(String[] args) {
       B b = new B("jack");
       b.sayHello();
    }
}

抽象类主要是用于简化代码结构,这里我更愿意把抽象类和接口进行比较,抽象类和接口一样为我们提供了严格的标准,我们在抽象父类里定义了方法,在子类中必须实现,试想这样一个场景,一个people必须会吃喝拉撒睡,那么我们定义了一个抽象父类里面抽象了这个5个方法,那么在子类中我们必须实现它否则就不能算是一个完整的人

补充*.模板方法设计模式

这里只介绍这种思路

模板方法设计模式:
解决模板方法设计模式解决问题: 编写某一个方法的时候,发现有大量重复代码,但是有小部分是不一样,那么就可以使用模板方法设计模式。
需求:
1. 定义一个学生类,学生有唱歌功能,学生唱歌: 先点歌 , 学生开始唱歌 , 谢谢。
2. 定义一个主播类,主播有唱歌功能,主播唱歌: 先点歌 , 主播跳着舞、唱歌 , 谢谢。
模板方法的固定步骤:
1. 定义一个抽象类
2 在抽象类中定义两个方法
a. 把相同部分的代码放在一个方法里面
b. 把不同部分的代码单独抽离一个抽象方法,让子类去实现

4.接口

接口: java提供一个关键字interface用于定义接口的, 接口其实本质上就是一个特殊类的。
定义接口的格式:

interface  接口名{
   // 成员变量(常量)
   // 成员方法(抽象方法)
}

使用接口要注意的事项:
1. 接口是不能创建对象的, 接口是被实现的,一个类可以实现多个接口
2. 一个非抽象类实现接口的时候必须要把接口中的所有抽象方法全部重写(实现)

前面的叙述中我们已经提到了接口,类似抽象类,接口我们看下面一个例子,便可得知接口的作用

4.1 接口的定义

interface Role {
    public void greet();
    public void move();
    public int getSpeed();
}

4.2 接口的继承

//每个接口可以继承多个接口,使用关键字extends继承
interface Hero extends Role {
    public void attack();
}

4.3 接口的实现

// 每个类可以实现多个接口
class Zeus implements Hero {
    private final String name = "Zeus";
    public void attack() {
        System.out.println(name + ": Attack!");
    }

    public void greet() {
        System.out.println(name + ": Hi!");
    }

    public void move() {
        System.out.println(name + ": Move!");
    }

    public int getSpeed() {
        return 10;
    }
}

4.4 接口的多态

class Athena implements Hero {
    private final String name = "Athena";
    public void attack() {
        System.out.println(name + ": Attack!");
    }

    public void greet() {
        System.out.println(name + ": Hi!");
    }

    public void move() {
        System.out.println(name + ": Move!");
    }

    public int getSpeed() {
        return 10;
    }
}

public class Main {
    public static void main(String[] args) {
        Hero[] heros = {new Zeus(), new Athena()};
        for (Hero hero: heros) {
            hero.greet();
        }
    }
}
//我们会一次输出 Zeus:hi! 
               Athena: hi!
    可见类其实也是有多态的

补充说明一下几个新特性,因为目前我的学习中使用的不多
jdk1.8开始接口新增三个新特性:
1. 接口可以写默认方法,接口的默认方法需要接口的实现类去调用。
2. 接口增加私有方法,私有方法只能在接口内部去使用
3. 静态方法, 接口中静态方法只能使用接口去调用。

类与接口、接口与接口之间关系:
类与接口的关系是实现。 implements
接口与接口之间的关系继承。 extends

要注意的事项:
1. 一个接口是可以继承多个接口的。
2、一个接口继承多个接口,如果多个接口中存在同名的默认方法,那么则不支持多继承!!!
3、一个类实现多个接口,如果多个接口中存在方法签名冲突,则此时不支持多实现。
4、一个类继承了父类,又同时实现了接口,父类中和接口中有同名的默认方法,实现类会优先用父类的。
5、一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可

5. 内部类

5.1 成员内部类*(不是重点)

内部类: 一个类的内部定义了另外一个类,那么另外一个类就是称作为内部类。
成员内部类: 在一个类的成员位置定义另外一个类,该类就称作为成员内部类。
格式:

 class 外部类{
      class 内部类{
      }
 }

成员内部类创建的对象格式:
外部类.内部类 变量名 = new 外部类().new 内部类();
成员内部类的访问特点:
1. 成员内部类的实例方法中,同样可以直接访问外部类的实例成员、静态成员
2。 如果内部类的方法访问成员变量,而外部类的成员与内部类的成员同名的情况下,
默认采用的就是就近原则机制去访问,如果需要指定访问外部类的成员: 外部类.this.成员名

来看下面这个例子

public class Outer {

    //a=100这个变量什么时候存在内存中?  创建外部类的对象的时候该成员变量才存在的。
    int a =100;

    static String color="红色"; //静态成员变量

    int age = 100; //与内部类的age同名了。

    class Inner{

        //成员变量
        int age = 20;

        String name = "内部类";

        //内部类的对象
        ```Java
        public void print(){
            System.out.println("姓名:"+ name+" age:"+ Outer.this.age); 
            System.out.println("a:"+ a+" 颜色:"+ color+" age:"+age); 
        ```
        }
    }  //成员内部类
}

public class Test {

    public static void main(String[] args) {
        //创建内部类的对象
        Outer.Inner  inner  = new Outer().new Inner();
        inner.print();
    }
}

5.2 静态内部类

其实其本质就是非静态方法无法访问静态变量,静态内部类中的实例方法同样无法访问外部类中的非静态成员变量,这里结合代码,是比较好理解的

静态成员内部类访问特点:
静态成员内部类的实例方法是可以直接访问静态成员,没法直接访问非静态成员的。

静态成员内部类创建对象格式:

 外部类.内部类   变量名 = new  外部类.内部类();
public class Outer {

    //随着类的加载而存在的, 使用类名即可访问
    static String schoolName = "剑桥";

    //需要创建对象才存在。
    int height = 175 ;

    //静态成员内部类
    static class Inner{
        int age = 10;


        //静态成员内部类的实例方法
        public void print(){
            System.out.println("age = "+age);
            //静态成员内部类的实例方法是没法直接访问外部类的非静态成员变量。
           // System.out.println("height = "+height);
            System.out.println("schoolName = "+schoolName);
        }
    }
}

public class Test {

    public static void main(String[] args) {
        Outer.Inner inner = new Outer.Inner();
        inner.print();
    }
}
5.3 匿名内部类

匿名内部类作为一种快捷的写法,还是很有必要掌握的,我们知道对于抽象类,我们必须继承并且实现抽象父类中的所有方法才能在主函数中创造实例调用,但是匿名抽象类允许我们在不额外单独写一个实现类实例的情况下,在主函数中就可以对抽象类调用,这种没有类名的局部内部类,就是匿名内部类

匿名内部类: 没有类名的局部内部类。
需求: 创建一个Animal的子类对象,并且调用的cry方法。
作用: 更加方便的创建子类对象。

创建匿名内部类的格式:
        new  父类|接口(){
            实现所有抽象方法 
        }
public abstract  class Animal {

    public abstract void cry(); //叫
}

//普通类...
public class Dog extends  Animal{


    @Override
    public void cry() {
        System.out.println("在吗?在吗?在吗?我们现在到底什么关系?");
    }
}

public class Test {

    public static void main(String[] args) {
        //实现的方式一: 创建类的写法
//        Dog dog = new Dog();
//        dog.cry();

        //方式二: 使用匿名内部类去实现。
        Animal animal = new Animal(){  //父类   变量名 = new  子类对象() 多态形式。

            @Override
            public void cry() {
                System.out.println("在吗在吗?");
            }
        }; //这里就是一个Animal的子类对象,只不过你没有看到类名。

        animal.cry();
    }
}

再来看看这种情况,我们试着不去new一个新的对象,而是直接在调用方法即可

public interface Swimming {

    public void swim();
}
public class Test {

    public static void main(String[] args) {
      Swimming dog =  new Swimming(){

            @Override
            public void swim() {
                System.out.println("狗刨式游泳...");
            }
        };

        go(dog); //要接口实现类对象,我就使用匿名内部类的方式去实现。

        go(new Swimming() {
            @Override
            public void swim() {
                System.out.println("鸭子游啊游...");
            }
        });

    }
    //匿名内部类场景的场景:调用方法的时候传递参数。
    public static void  go(Swimming swimming){ //这里参数需要传递该接口的实现类对象。
        swimming.swim();
    }
}
*作者功力尚浅,还是无法体会到匿名内部类带来的书写便捷,暂时运用场景不多(感觉很鸡肋)*
5.4 枚举类

枚举: 枚举是一种特殊结构类。

定义枚举的格式:

    enum 类名{
        枚举值,枚举值..
    }

枚举类要注意的事项:
1. 枚举值默认的修饰符 public static final。枚举值本身就是一个常量
2. 枚举值其实本质上就是枚举类的对象。
3. 枚举类的构造器是私有化,枚举类对外是不能创建对象。
4. 枚举类都是final修饰的,不能被继承。
5. 枚举类中,第一个语句必须是枚举值。
6. 所有枚举类都继承enum类,为枚举类添加values() 与valuesOf方法。

values() .:返回枚举类的所有枚举值
valueOf() : 根据枚举值的名字获取枚举类。

//枚举类的应用场景: 枚举类应用场景往往是用于参数的传递,并且要求参数必须是有约束(必须是固定范围内的数据)
ppublic enum Gender {

    MAN,WOMAN;
}
public class Test {


    public static void main(String[] args) {
        String name="张三";
        Gender sex =Gender.WOMAN;
        showBook(name,sex);
    }

    public static void showBook(String name,Gender sex){
        if(sex==Gender.WOMAN){
            System.out.println("欢迎"+name+"小姐姐进入到我们的图书商城..");
        }else if(sex==Gender.MAN){
            System.out.println("欢迎"+name+"大哥哥进入我们的图书商城");
        }
    }
}

6. 泛型

类似于C++的template,Java的类和接口也可以定义泛型,即同一套函数可以作用于不同的对象类型。
泛型只能使用对象类型,不能使用基本变量类型。

6.1 举例:泛型类

泛型的作用:泛型提供了在编译阶段约束所能操作的数据类型,并自动进行检查的能力!这样可以避免强制类型转换,及其可能出现的异常。

泛型的本质:把具体的数据类型作为参数传给类型变量

泛型类的定义格式:

class 类名<标识符>{

}

注意的事项:

  1. 自定义泛型是是需要先声明类型的变量,类型的变量符合标识符的命名规范即可,一般我们都使用一个大写字符表示。
    比如:E
  2. 类上的泛型变量是在创建对象的时候确定具体的数据类型的

需求: 自定义一个ArrayList,要求: 1. 创建对象的时候确定操作的数据类型, 2.自定义ArrayList可以添加任意类型的数据,
获取元素的时候不需要进行强制类型转换。

    public class MyArrayList<E> {  //声明数据类型的变量 ,数据类型的占位符。

    //数据可以存储到该数组中
    private Object[] arr = new Object[10];

    //定义一个变量记录索引值的位置
    private int index = 0;


    public void add(E o){ //Object类是所有类的父类,那么就可以存储所有类型的数据。 多态
        arr[index] =o; //把元素存储到数组中。
        index++;
    }

    public  E get(int index){ //返回值类型如果写Object,那么也可以返回所有类型的数据,因为多态。
        return (E)arr[index];
    }
}
public class Test1 {


    public static void main(String[] args) {
       //创建我自定义集合类对象
        MyArrayList<String> list = new MyArrayList<>();
        list.add("狗娃");
        list.add("狗剩");
        list.add("铁蛋");

        String item = list.get(1);
        System.out.println(item);

    }
}
6.2 泛型的上下限

泛型上下限:

? : 代表了任意类型。 ?一般我们是不会单独去使用的,一般我们都会配合泛型上下限去使用。
? extends Car: 泛型上限,传入的数据必须是Car或者是Cart的子类元素
? super Car: 泛型下限 参数必须是car或者是Car父类。

结合下面这这个例子来看非常好理解,补充一点:object类是所有类的父类

import java.util.ArrayList;

class Car{} //车

class BMW extends  Car{} //宝马

class BENZ extends  Car{} //大奔


public class Test {

    public static void main(String[] args) {
        ArrayList<Car> list1 = new ArrayList<>();
        ArrayList<BMW> list2 = new ArrayList<>();
        ArrayList<BENZ> list3 = new ArrayList<>();
        ArrayList<String> list4 = new ArrayList<>();
        ArrayList<Object> list5 = new ArrayList<>();
        print1(list1);
        print1(list2);
        print1(list3);

        //===========泛型上限========
        print2(list1);
        print2(list2);
        print2(list3);


        //=========泛型下限====
        print3(list1);
        print3(list5);


    }

    public static void print1(ArrayList<?> list){  //接收一个集合对象,集合的元素可以是任意类型的数据

    }

    public static void print2(ArrayList<? extends  Car> list){}
    public static void print3(ArrayList<? super Car> list){}

}

7.object和objects

Object类:Object类是Java中所有类的祖宗类,因此,Java中所有类的对象都可以直接使用Object类中提供的一些方法。

常用的方法:
       toString()  返回对象的字符串表示形式。
       equals()   对比指定的对象是否为同一个。
       clone()    克隆一个对象


浅克隆:克隆一个对象的时候,如果该对象有其他引用类型的对象,是不会克隆的,克隆只是简单类型的数据。
    1. 如果一个对象需要进行克隆,那么完成以下两个步骤:
            a. 克隆的对象所属的类必须要实现一个Cloneable接口, Cloneable接口是没有方法的,我们称作为标记接口。
            b. 重写clone方法,修改权限修饰符。
深克隆;克隆一个对象的时候,不管对象的属性是简单类型还是引用类型都克隆。
public class Person implements  Cloneable {

    private String name;

    private int age;

    private double[] scores; //数组对象,


    public Person(String name, int age, double[] scores) {
        this.name = name;
        this.age = age;
        this.scores = scores;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double[] getScores() {
        return scores;
    }

    public void setScores(double[] scores) {
        this.scores = scores;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
       Person p = (Person) super.clone();
        p.setScores(p.getScores().clone()); //获取到引用类型的对象,然后再次克隆
        return p;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", scores=" + Arrays.toString(scores) +
                '}';
    }
}·
import java.util.Objects;

public class Student {

    private int id;

    private String name;

    public Student(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public Student() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }


    /*
        s1: this
        s2:  参数
     */
//    @Override
//    public boolean equals(Object o) {
//      // 强制类型转换
//       Student s2 = (Student) o;
//       return this.id==s2.id && this.name.equals(s2.name);
//    }


    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return id == student.id && Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}
public class Test {

    public static void main(String[] args) {
        Student s1 = new Student(110,"狗娃");
        String str = s1.toString();
        System.out.println("toString方法:"+ str);
        System.out.println("直接打印一个变量:"+ s1); //直接输出s1是地址...



    }
}
public class Test3 {

    public static void main(String[] args) throws CloneNotSupportedException {
        double[] arr = {11,12,13};
        Person p1 = new Person("陈小狗",30,arr);
        Person p2 = (Person) p1.clone(); //alt+enter 抛出异常
        double[] scores = p1.getScores();
        scores[1]=6666;

        System.out.println("p1:"+ p1);
        System.out.println("p2:"+ p2);


    }
}
public class TestEquals {

    public static void main(String[] args) {
        Student s1 = new Student(110,"狗娃");
        Student s2 = new Student(110,"狗娃");
        System.out.println("是同一个对象吗? "+ (s1.equals(s2)));
    }
}

而objects类是一个工具类,里面有几个方法我们需要掌握

equals(Object o1, Object o2) 判断两个对象是否为同一个对象。

isNull() 判断一个变量是否为null值。  null 返回true,

nonNull()  判断一个变量是否为null值。
public class Test {

    public static void main(String[] args) {
        Student s1 = new Student(110,"a");
        Student s2 = new Student(110,"b");
        boolean result = Objects.equals(s1, s2);
        System.out.println("是同一个对象吗?"+ result);


        String str = null; //变量
        if(!Objects.isNull(str)) {
            System.out.println("等于admin?" + str.equals("admin"));  //NullPointerException 一个变量没有记录对象的地址,而使用了对象的方法或者属性。
        }
        System.out.println("该变量是否为空?"+ Objects.isNull(str));
    }

}

8. 基本数据类型和包装数据类型

要求是记忆即可

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

基本数据类型的弊端: 没有可以使用,因为不是对象。

基本数据类型的数据是可以自动装箱成为包装类型的。


自动装箱: 基本数据类型转换为引用类型的数据。 底层 依赖方法: valueOf

自动拆箱:引用类型的数据自动转换为基本类型。 底层依赖的方法: intValue()

9.StringBuilder和StringJoiner

StringBuilder: 是一个字符串的容器,它里面装的字符串是可以改变的,就是用来操作字符串的

创建对象:
new StringBuilder() 创建一个空白的字符串对象,不包含任何的内容的。
new StringBuilder(str) 创建指指定字符串内容的字符串对象。

常用的方法:
append() 添加
reverse() 翻转
length() 返回字符个数
toString() 把StringBuilder转换为String

    public class Test1 {

    public static void main(String[] args) {
        //创建一个StringBuilder
        StringBuilder sb = new StringBuilder();
        //添加内容
        sb.append("您好,");
        sb.append("大哥");

        //翻转
        sb.reverse();
    }
    }

StringJoiner这里不多多做赘述,与StringBuilder类似

10.工具类

10.1 math工具类
public class Test {

    public static void main(String[] args) {
        System.out.println("绝对值:"+ Math.abs(-1));  //  1
        System.out.println("绝对值:"+ Math.abs(3));  //  3
        System.out.println("向上取整:"+ Math.ceil(3.14));  //  找到第一个比它大的整数          4
        System.out.println("向上取整:"+ Math.ceil(-3.14));  //  找到第一个比它大的整数   //ceil就是天花板,所以找找比他高的      -3
        System.out.println("向下取整:"+ Math.floor(3.14));  //  找到第一个比它小的整数    //floor是地板,找比他低的     3
        System.out.println("向下取整:"+ Math.floor(-3.14));  //  找到第一个比它小的整数         -4
        System.out.println("比较两个数的最大值:"+ Math.max(3,2));  // 3
        System.out.println("2的3次幂:"+ Math.pow(2,3));  // 2^3 = 8
        System.out.println("四舍五入:"+ Math.round(3.14));  // 四舍五入
        System.out.println("随机数,随机数范围是0.0 ~1.0 :"+ Math.random()*10);  //

    }

}
10.2 日期类

Date
SimpleDateFormat
LocalDateTime
DateTimeFormatter

public class Test {

    public static void main(String[] args) {
        Date date =  new Date();  //alt+enter
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("当前时间:"+ simpleDateFormat.format(date));
        //字符串转换 日期...  parse


        LocalDateTime localDateTime = LocalDateTime.now();
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        System.out.println("当前时间:"+ dateTimeFormatter.format(localDateTime));

        字符串转换 日期...  parse
//        LocalDateTime.parse(字符串,日期格式化对象)

    }
}
10.3

对于工具类的使用,我们可以将各种方法放在一个同类名的文件下,作为工具类放入同级目录下的一个untils包内,再在我们的调用文件中导入就好了,导入路径就是文件目录结构,记得public工具类,这样有利于我们的工程层级清晰,将文件拆解也有利于维护。

11.lambda表达式

Java Lambda表达式的一个重要用法是简化某些匿名内部类(Anonymous Classes)的写法。实际上Lambda表达式并不仅仅是匿名内部类的语法糖,JVM内部是通过invokedynamic指令来实现Lambda表达式的。具体原理放到下一篇。本篇我们首先感受一下使用Lambda表达式带来的便利之处
Lambda and Anonymous Classes
如何使用Lambda表达式简化匿名内部类的书写,但Lambda表达式并不能取代所有的匿名内部类,只能用来取代函数接口(Functional Interface)的简写。我们来看几个例子:

例子1:无参函数的简写

如果需要新建一个线程,一种常见的写法是这样:

// JDK7 匿名内部类写法
new Thread(new Runnable(){// 接口名
	@Override
	public void run(){// 方法名
		System.out.println("Thread run()");
	}
}).start();

上述代码给Tread类传递了一个匿名的Runnable对象,重载Runnable接口的run()方法来实现相应逻辑。这是JDK7以及之前的常见写法。匿名内部类省去了为类起名字的烦恼,但还是不够简化,在Java 8中可以简化为如下形式:
// JDK8 Lambda表达式写法
new Thread(
() -> System.out.println(“Thread run()”)// 省略接口名和方法名
).start();
上述代码跟匿名内部类的作用是一样的,但比匿名内部类更进一步。这里连接口名和函数名都一同省掉了,写起来更加神清气爽。如果函数体有多行,可以用大括号括起来,就像这样:

// JDK8 Lambda表达式代码块写法
new Thread(
() -> {
System.out.print(“Hello”);
System.out.println(" Hoolee");
}
).start();
例子2:带参函数的简写

如果要给一个字符串列表通过自定义比较器,按照字符串长度进行排序,Java 7的书写形式如下:

// JDK7 匿名内部类写法
List<String> list = Arrays.asList("I", "love", "you", "too");
Collections.sort(list, new Comparator<String>(){// 接口名
    @Override
    public int compare(String s1, String s2){// 方法名
        if(s1 == null)
            return -1;
        if(s2 == null)
            return 1;
        return s1.length()-s2.length();
    }
});

上述代码通过内部类重载了Comparator接口的compare()方法,实现比较逻辑。采用Lambda表达式可简写如下:

// JDK8 Lambda表达式写法
List<String> list = Arrays.asList("I", "love", "you", "too");
Collections.sort(list, (s1, s2) ->{// 省略参数表的类型
    if(s1 == null)
        return -1;
    if(s2 == null)
        return 1;
    return s1.length()-s2.length();
});

上述代码跟匿名内部类的作用是一样的。除了省略了接口名和方法名,代码中把参数表的类型也省略了。这得益于javac的类型推断机制,编译器能够根据上下文信息推断出参数的类型,当然也有推断失败的时候,这时就需要手动指明参数类型了。注意,Java是强类型语言,每个变量和对象都必需有明确的类型。

简写的依据
也许你已经想到了,能够使用Lambda的依据是必须有相应的函数接口(函数接口,是指内部只有一个抽象方法的接口)。这一点跟Java是强类型语言吻合,也就是说你并不能在代码的任何地方任性的写Lambda表达式。实际上Lambda的类型就是对应函数接口的类型。Lambda表达式另一个依据是类型推断机制,在上下文信息足够的情况下,编译器可以推断出参数表的类型,而不需要显式指名。Lambda表达更多合法的书写形式如下:

// Lambda表达式的书写形式
Runnable run = () -> System.out.println("Hello World");// 1
ActionListener listener = event -> System.out.println("button clicked");// 2
Runnable multiLine = () -> {// 3 代码块
    System.out.print("Hello");
    System.out.println(" Hoolee");
};
BinaryOperator<Long> add = (Long x, Long y) -> x + y;// 4
BinaryOperator<Long> addImplicit = (x, y) -> x + y;// 5 类型推断

上述代码中,1展示了无参函数的简写;2处展示了有参函数的简写,以及类型推断机制;3是代码块的写法;4和5再次展示了类型推断机制。

自定义函数接口
自定义函数接口很容易,只需要编写一个只有一个抽象方法的接口即可。

// 自定义函数接口
@FunctionalInterface
public interface ConsumerInterface<T>{
	void accept(T t);
}

上面代码中的@FunctionalInterface是可选的,但加上该标注编译器会帮你检查接口是否符合函数接口规范。就像加入@Override标注会检查是否重载了函数一样。

有了上述接口定义,就可以写出类似如下的代码:

ConsumerInterface<String> consumer = str -> System.out.println(str);

进一步的,还可以这样使用:

class MyStream<T>{
	private List<T> list;
    ...
	public void myForEach(ConsumerInterface<T> consumer){// 1
		for(T t : list){
			consumer.accept(t);
		}
	}
}
MyStream<String> stream = new MyStream<String>();
stream.myForEach(str -> System.out.println(str));// 使用自定义函数接口书写Lambda表达式

注:该部分来自张乘辉老师的文章

12. 排序

关于排序算法我会单独出一篇文章,因为这里面还是有很多模板需要整理的

13. 正则表达式

这部分其实在使用的时候去搜索基本格式都有,只需要了解能看懂别人的表达式即可,下面是规则

范围词:
[abc] 出现字符必须是abc其中一个。
[^abc] 出现的字符不能有abc。
[a-z] 出现字符可以是a-z这个范围之内
[a-zA-Z0-9]出现字符可以是a-zA-Z0-9这个范围之内
注意: 范围词如果不配合数量词使用,那么范围词只能匹配一个字符。

预定义字符:
. 任何字符
\d 一个数字: [0-9]
\D 非数字: [^0-9]
\s 一个空白字符:
\S 非空白字符: [^\s]
\w [a-zA-Z_0-9]
\W [^\w] 一个非单词字符
\在java中称作为转移字符串, 是有着特殊含义的。
场景: 如果你需要输出一个特殊符号的时候,那么就需要使用\才能输出原样输出特殊符号。。

数量词:
X? X , 一次或0次
X* X,零次或多次
X+ X , 一次或多次
X {n} X,正好n次
X {n, } X,至少n次
X {n,m} X,至少n但不超过m次
常用的方法:
public boolean matches(String regex) 字符串匹配一个正则表达式,符合返回true,不符合返回false。

14. 常用容器

14.1 List

接口:java.util.List<>。

实现:

  • java.util.ArrayList<>:变长数组
  • java.util.LinkedList<>:双链表

函数:

  • add():在末尾添加一个元素
  • clear():清空
  • size():返回长度
  • isEmpty():是否为空
  • get(i):获取第i个元素
  • set(i, val):将第i个元素设置为val
14.2 栈

类:java.util.Stack<>

函数:

  • push():压入元素
  • pop():弹出栈顶元素,并返回栈顶元素
  • peek():返回栈顶元素
  • size():返回长度
  • empty():栈是否为空
  • clear():清空
14.3 队列

接口:java.util.Queue<>

实现:

  • java.util.LinkedList<>:双链表
  • java.util.PriorityQueue<>:优先队列
    默认是小根堆,大根堆写法:new PriorityQueue<>(Collections.reverseOrder())

函数:

  • add():在队尾添加元素
  • remove():删除并返回队头
  • isEmpty():是否为空
  • size():返回长度
  • peek():返回队头
  • clear():清空
14.4 Set

接口:java.util.Set

实现:

  • java.util.HashSet:哈希表
  • java.util.TreeSet:平衡树

函数:

  • add(元素):添加元素
  • contains(元素):是否包含某个元素
  • remove(元素):删除元素
  • size():返回元素数
  • isEmpty():是否为空
  • clear():清空
  • toArray() 把集合转化为Object类型数组

java.util.TreeSet多的函数:

  • ceiling(key):返回大于等于key的最小元素,不存在则返回null
  • floor(key):返回小于等于key的最大元素,不存在则返回null
14.5 Map

接口:java.util.Map<K, V>

实现:

java.util.HashMap<K, V>:哈希表
java.util.TreeMap<K, V>:平衡树

函数:

  • put(key, value):添加关键字和其对应的值
  • get(key):返回关键字对应的值
  • containsKey(key):是否包含关键字
  • remove(key):删除关键字
    *size():返回元素数
  • isEmpty():是否为空
  • clear():清空
  • entrySet():获取Map中的所有对象的集合
  • Map.Entry<K, V>:Map中的对象类型
  • getKey():获取关键字
  • getValue():获取值

java.util.TreeMap<K, V>多的函数:

  • ceilingEntry(key):返回大于等于key的最小元素,不存在则返回null
  • floorEntry(key):返回小于等于key的最大元素,不存在则返回null

注: 该部分出自yxc大佬,acw的java章节

15. 补充内容

15.1 集合
15.1.1的遍历

集合的遍历方式:

方式一:迭代器的方式

方式二: 增强for循环方式

方式三: lambda的表达式

获取迭代器方式:

iterator();   获取迭代器

迭代器的方法:

hashNext();  判断迭代器的指针当前有没有指向元素,有返回true,没有返回false。
next() :   获取指针当前指向的元素,然后指针向后移动一位。
public class Test1 {

    public static void main(String[] args) {
        Collection<String>  list = new ArrayList<>();
        list.add("古力娜扎");
        list.add("迪丽热巴");
        list.add("马尔扎哈");
        list.add("玛卡巴卡");
        //1 获取迭代器
        Iterator<String> it = list.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }

    }
}
//2.
for(String item:list){  //遍历出来的每一个元素都给item变量去记录。
            System.out.println(item);
        }
//3.
/*   list.forEach(new Consumer<String>() {

            // 等会foreach方法会遍历集合的每一个元素,你需要如何处理该元素,那么就在accept方法里面写上自己的逻辑即可。
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });*/

        //使用lambda表达式方式去简化
        list.forEach(s-> System.out.println(s));
    
15.1.2 集合的方法

add(元素) 添加元素到集合中
clear() 清空集合的元素
remove(元素) 删除集合中的元素
contains(元素) 判断集合的元素是否存在
isEmpty() 判断集合的元素是否为空
size() 查看集合的元素个数
toArray() 把集合转化为Object类型数组

15.2 list特有方法

List系列集合特点:有序、元素重复,有索引操作。

List集合特有的方法:

add(index,element) 指定索引值插入元素
remove(index)  根据索引值去删除元素, 返回被删除的元素
set(index,element) 根据索引值设置指定位置的元素(修改)
get(index) 根据索引值获取元素。
public class Test1 {

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("啊Q");
        list.add("啊甘");
        list.add("啊强");
        // 指定索引值插入元素
        list.add(1,"阿珍");
        // 根据索引值去删除元素
        System.out.println("删除成功吗?"+ list.remove(3));
        //修改指定位置的元素
        list.set(1,"凤娇");
        System.out.println("集合元素:"+ list);
        //根据索引值获取元素
        System.out.println("根据索引值获取阿甘:"+ list.get(2));

        //list集合特有一种遍历方式, fori
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }
}
15.2.1 ArrayList

ArrayList的底层就是使用Object数组去实现的, 数组的内存地址是连续的,可以根据索引值查询速度是飞快,增删效率比较低。

应用场景:
    适用场景: 数据量不是很大,而且是根据索引查询较多,增删比较少,那么就可以选用ArrayLIst。
    不适用场景: 数据量比较大,增删频率又是比较高。
15.2.2 Linklist

LinkedList特点:
底层是基于双向链表数据结构去实现的, 查询效率低, 因为链表数据结构查询元素都是从头开始一个一个遍历的。
增删效率高,因为只需要修改三个元素地址。

15.3 set特有方法

Set特点: 无序、元素是不可重复、无索引操作

HashSet
LinkedHashSet
TreeSet
15.3.1 hashset

HashSet底层是如何实现?为什么是无序、不可重复是依赖了什么方法判断的?

HashSet底层依赖哈希表去实现的。

jdk8之前 : 哈希表 = 数组+链表
jdk8开始:  哈希表 = 数组 + 链表+ 红黑树

存储元素的时候链表转换为红黑树的数据结构是根据什么条件:

链表长度是大于8,数组的长度大于等于64.

判断是否为重复元素依赖: equals方法。 存储元素在指定位置依赖hashCode方法。

hash值:

每一个对象都有一个HashCode, 同一个对象不管是什么时候调用得到的值都是一致的。不同对象的HashCode一般都不会一致。
public class Student {

    private int id;

    private String name;

    private int age;

    public Student(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

   // get and set...

    @Override
    public boolean equals(Object o) {
        Student s = (Student) o;
       return this.id == s.id;
    }

    @Override
    public int hashCode() {
        return this.id;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Test {

    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>();
        set.add("蔡徐坤");
        set.add("小黑子");
        set.add("大黑豆");
        System.out.println("添加成功吗?"+set.add("大黑豆"));
        System.out.println("集合的元素: "+ set);

        Student s1 = new Student(110,"狗娃",18);
        Student s2 = new Student(220,"狗剩",19);
        Student s2 = new Student(220,"小明",21);
        System.out.println("s1的hashCode:"+ s1.hashCode());
        System.out.println("s2的hashCode:"+ s2.hashCode());
        System.out.println("集合元素:"+ set);
    }
}
// 可以看到输出结果中第一项的“大黑豆”添加失败
// 第二项中我们通过在类中改写hashcode和equal方法,让对象的id属性作为比较的参考物,从而输出结果中id存在只存在一个110和一个220狗剩,`重复的id220小明会被丢弃`
15.3.2 linkhashset

LinkedHashSet : 有序、元素不可重复,无索引

LinkedHashSet是在HashSet基础上可以保持插入顺序的一种Set集合。

底层原理

是直接继承了HashSet,而HashSet又是HashMap包装了一层,而LinkedHashMap是HashMap的子类,所以HashSet的实现其实是依赖了LinkedHashMap。

构造方法

总共有四个构造方法,会发现其实是调用到了HashSet中一个特殊的构造方法中,这个方法初始化的不是HashMap而是LinkedHashMap。

LinkedHashSet就是在HashMap、HashSet和LinkedHashMap的基础上进行了下封装,没有加任何变化。
在保证元素唯一性的情况下还可以保证遍历顺序是插入顺序。

15.3.3 treeset

TreeSet : 不可重复, 无索引,可排序。

排序的规则:

  1. 如果元素本身有自然顺序,那么按照元素的自然顺序排序。 比如: abc 123
  2. 如果是字符串元素,那么就按照字符串的比较规则去排序:

a. 比较两个字符串首个不同字符
b. 如果两个字符串不存在不同的字符,那么比较就是长度。

TreeSet如果存储自定义类型对象的时候,因为自定义对象本身是不具备比较规则的,解决方案有两种:

1. 对象所属的类去实现COmparable接口,把比较的规则卸载CompareTo方法里面。
2. 创建TreeSet对象的时候传入比较器对象

TreeSet是否为同一个元素底层不是依赖hashCode与equals方法判断的,依赖比较规则的方法判断的,如果比较返回的是0则为是同一个元素

public class Student /*implements  Comparable<Student>*/ {

    private int id;

    private String name;

    private int age;

    public Student(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
get() and set()...

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

//    @Override
//    public int compareTo(Student o) {
//        return o.age-this.age;
//    }
}

public class Test2 {

    public static void main(String[] args) {
        TreeSet<Student> treeSet = new TreeSet<>((Student o1, Student o2)->{
                return o1.getAge()-o2.getAge();
            }
        );
        //添加元素
        treeSet.add(new Student(1,"张三",18));
        treeSet.add(new Student(2,"李四",7));
        treeSet.add(new Student(3,"王五",28));
        treeSet.add(new Student(4,"王八",14));
        treeSet.add(new Student(5,"老六",14));

        System.out.println("集合的元素:"+ treeSet);
    }
}
15.3.4并发修改异常

并发修改异常:
在使用迭代器遍历的时候,不允许使用集合对象去修改的元素的个数(增加与删除), 如果你要删除,那么请使用迭代器的自带remove方法去删除。

public class Test1 {

    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("aa");
        list.add("bb");
        list.add("cc");
        list.add("dd");

        //使用迭代器去遍历
//        Iterator<String> it = list.iterator();
//        while(it.hasNext()){
//            System.out.println(it.next());
//          //  list.set(1,"呵呵");
//           it.remove();
//        }

     /*   for (String s : list) {
            System.out.println(s);
            list.add("aa");
        }*/

        //需求:  从集合中删除bb元素,使用迭代器遍历集合的元素,判断每一个元素是否为bb,如果是则删除
        for (String s : list) {
            if(s.equals("bb")){
                list.remove(s);
                 break;
            }
        }


        System.out.println("集合的元素:"+ list);
    }
}
15.4 map
15.4.1 hashmap

HashMap底层原理:
jdk8之前: 键基于哈希表实现, 哈希表=数组+链表
jdk8开始: 键基于哈希表实现, 哈希表= 数组+链表+红黑树
判断key重复元素的关键: hashCode方法与equals方法。

15.4.2 treemap

特点:不重复、无索引、可排序(按照键的大小默认升序排序,只能对键排序)
原理:TreeMap跟TreeSet集合的底层原理是一样的,都是基于红黑树实现的排序

15.4.3 map的遍历

Map集合方式方式有三种:

1. 键找值   keySet() + get(key)

2. 键值对方式   难度最大

3、 通过lambda表达式去遍历,  最简单、最方便。
public class Test1 {

    public static void main(String[] args) {
        Map<String, Double> map = new HashMap<>();
        map.put("蜘蛛精", 169.8);
        map.put("紫霞", 165.8);
        map.put("至尊宝", 169.5);
        map.put("牛魔王", 183.6);

        //通过keySet方法去遍历
        Set<String> keys = map.keySet();
        //遍历所有的key
        for (String key : keys) {
            Double value = map.get(key); //通过key得到value
            System.out.println("键:"+ key+" 值:"+ value);
        }
    }
}
//方式方式二: 使用entrySet方法去实现
        Set<Map.Entry<String, Double>> entrySet = map.entrySet();

        //使用增强for循环遍历
        for (Map.Entry<String, Double> entry : entrySet) {
            System.out.println("key:"+ entry.getKey()+" value:"+entry.getValue());
        }
map.forEach((key,value)->{
            System.out.println("key:"+ key +" 值:"+ value);
        });

// 如有疑问欢迎交流,如有错误感谢指正

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值