Java学习笔记---面向对象编程

 2021.8.23

面向对象基础

方法

【private  field 私人字段(只允许内部调用)/   public field公共字段(外部可调用)】

class Person {             //为了避免外部直接访问field
    private String name;   //使用了privatea,拒绝外部访问
    private int age;       //但是private允许类内部调用
}        //所以要想改变里面的值,一般在类里面再构造一个函数调用进行更改

【调用方法】

public class Main {
    public static void main(String[] args) {
        Person ming = new Person();
        ming.setName("Xiao Ming"); // 设置name
        System.out.println(ming.getName());
    }
}
class Person {
    private String name;
    private int age;

    public String getName() {
        return this.name;
    }

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

private方法】 :定义private方法的理由是内部方法是可以调用private方法的

this变量】:类似python的self,指向当前实例,没有命名冲突时可以省略。

【方法参数】

 public void setNameAndAge(String name, int age){}

【可变参数】:类型...

 public void setNames(String... names) {}

参数绑定】 

  • 当类调用的是基本值类型时,基本值改变,不会改变类的局部变量的值
  • 当类调用的是引用类型参数时(如列表),类型参数内部的改变,会同时改变类的局部变量,因为两者同时指向的是一个对象,任何一边改变,都会影响对方。
  •  String为不可变类,会用一个新的地址引用

构造方法

【初始化实例】:通过在内部构造一个名字相同的方法,就可以在实例化时一起初始化

public class Main {
    public static void main(String[] args) {
        Person p = new Person("Xiao Ming", 15);
}
class Person {
    private String name;
    private int age;

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

【默认构造方法】 :没有定义时,系统会默认构造

class Person {
    public Person() {
    }
}
  • 字段的初始化时null,基本数组类型时默认值

  • 既可以在类中,设置参数初始值,也可以在实例化时传入参数,运行顺序

    • 先初始化字段,int age = 10;

    • 再执行构造方法的代码进行初始化,new Person("Xiao Ming", 12)。

多构造方法】:存在多个构造方法,根据传入的参数和类型来判断使用哪个构造方法,也有通过调用其他构造方法,语法是this(...)

class Person {
    private String name;
    private int age;

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

    public Person(String name) {
        this(name, 18); // 调用另一个构造方法Person(String, int)
    }

    public Person() {
        this("Unnamed"); // 调用另一个构造方法Person(String)
    }
}

方法重载

【同名方法】:功能类似的方法使用同一名字,这就就叫overload。比如string类中的index0f,就可以查字符的,字符串的。

  • int indexOf(int ch):根据字符的Unicode码查找;

  • int indexOf(String str):根据字符串查找;

继承

【extends关键词】:子类自动获得父类的所有字段,严禁定义与父类重名的字段!

class Person {
    private String name;
    private int age;
}
class Student extends Person {
    // 不要重复name和age字段/方法,
    // 只需要定义新增score字段/方法:
    private int score;

    public int getScore() { … }
    public void setScore(int score) { … }
}

object类】:再没有extends 对象时,会默认继承object类 

protect关键词】:一般的继承,子类无法访问父类中的private,如果需要访问的话,要把private改为protected修饰。

super关键词】:

  • 可以理解为表示父类。
  • 在任何class中,第一行语句必须时调用父类,没有的话,系统会自动补上super(),没有任何参数在,如果父类没有默认构造方法,需要参数的话,就会报错。
    public class Main {
        public static void main(String[] args) {
            Student s = new Student("Xiao Ming", 12, 89);
        }
    }
    
    class Person {
        protected String name;
        protected int age;
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    }
    
    class Student extends Person {
        protected int score;
    
        public Student(String name, int age, int score) {
            //系统默认补上super(),但是会报错,
            //因为Person的构造方法需要传入name ,age两个参数. 没有默认构造方法
            //正确要的时:super(name,age)
            this.score = score;
        }
    }

【阻止继承】 :感觉现阶段应该很少用。

向上转型

  • 把一个子类类型安全的变为父类类型的赋值。
Student s = new Student();
Person p = s; // upcasting, ok    //一个person类的变量 指向studnet
Object o1 = p; // upcasting, ok   //一个object类的变量,指向person
Object o2 = s; // upcasting, ok          继承树 student ---》 person ---->object

向下转型】

  • 把一个父类类型强制转为子类类型
Person p1 = new Student(); // upcasting, ok
Person p2 = new Person();
Student s1 = (Student) p1; // ok
Student s2 = (Student) p2; // runtime error! ClassCastException!
//实际上p2指向的时person,而p1指向的student,因为子类会有比父类多更多的功能,转型就会出问题
  •  isstanceof操作符,用与判断一个实例是不是该类型,从Java 14开始,判断instanceof后,可以直接转型为指定变量,无需再强制转型。
    Object obj = "hello";
    if (obj instanceof String) {
        String s = (String) obj;
        System.out.println(s.toUpperCase());
    }
    -------------------------------------------------
    public class Main {
        public static void main(String[] args) {
            Object obj = "hello";
            if (obj instanceof String s) {
                // 可以直接使用变量s:
                System.out.println(s.toUpperCase());
            }
        }
    }

【组合和继承】

  • 子类和父类的关系是is,has关系不能用继承
  • 比如书有名字,学生有名字,人有名字,但是学生继承书的名字不太合理,可以在学生里面实例化一个书。

多态

【覆写override】:子类和父类的方法名相同,方法参数相同,返回值相同,就是覆写。

多态】:可以允许添加更多的子类实现更多的功能,而没改动父类的代码,要反复理解廖老师的例子中的income有多个态。

【覆写基类object方法】:一般覆写object的tostring,equals,hashcode。

调用super】:如果要调用父类的被覆写方法,要super来调用

class Person {
    protected String name;
    public String hello() {
        return "Hello, " + name;
    }
}

Student extends Person {
    @Override
    public String hello() {
        // 调用父类的hello()方法:
        return super.hello() + "!";
    }
}

 【final关键词】

可以不允许子类对自己某个方法进行覆写

可以不允许其他类继承自己

可以不允许对该值进行修改

class Person {   //子类无法覆写hello
    protected String name;
    public final String hello() {
        return "Hello, " + name;
    }
}

final class Person {  //此Person无法被继承
    protected String name;
}

class Person {     // name不允许被修改
    public final String name = "Unamed";
}

抽象类

多态的存在,每个子类都会对父类的方法进行覆写,使得父类的该方法显得比较没有意义,但是又不能删除,所以出现了抽象这个概念

【abstract】:如果一个class定义了方法,但没有具体执行代码,这个方法就是抽象方法,用abstract修饰。

abstract class Person {   //抽象类是 不能被实例化的,只能被继承
    public abstract void run();
}

面向抽象编程的本质就是:

  • 上层代码只定义规范(例如:abstract class Person);

  • 不需要子类就可以实现业务逻辑(正常编译);

  • 具体的业务逻辑由不同的子类实现,调用者并不关心。

接口

如果一个抽象类没有字段(一些类型的申明),所有方法全部都是抽象方法,那么可以把抽象类改写为接口interface,接口不可以实例化

abstract class Person {
    public abstract void run();
    public abstract String getName();
}
//改写
interface Person {
    void run();
    String getName();
}

 【继承关系】:建议反复观看

【dafualt方法】:在接口中,可以定义dafualt方法

public class Main {
    public static void main(String[] args) {
        Person p = new Student("Xiao Ming");
        p.run();
    }
}

interface Person {
    String getName();
    default void run() {
        System.out.println(getName() + " run");
    }
}

class Student implements Person {
    private String name;

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

    public String getName() {
        return this.name;
    }
}

dafualt方法的目的就是,当我们需要给接口添加一个新方法时,会对涉及的子类都进行更改,如果新增的是dafualt,那么子类就不需要全部修改,只在需要的子类进行覆写就好。

dafault方法和抽象类的普通方法有所不同,因为interface没有字段,default无法访问字段。

静态字段和静态方法

【static field】:静态字段有一个共享的空间

public class Main {
    public static void main(String[] args) {
        Person ming = new Person("Xiao Ming", 12);
        Person hong = new Person("Xiao Hong", 15);
        ming.number = 88;
        System.out.println(hong.number);
        hong.number = 99;
        System.out.println(ming.number);
    }
}

class Person {
    public String name;
    public int age;

    public static int number;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
// 88 99

实例可以访问静态字段,实际上是它们指向的是person class的静态字段。

因此,不推荐用实例变量.静态字段去访问静态字段,因为在Java程序中,实例对象并没有静态字段。在代码中,实例对象能访问静态字段只是因为编译器可以根据实例类型自动转换为类名.静态字段来访问静态对象。

推荐用类名来访问静态字段。可以把静态字段理解为描述class本身的字段(非实例字段)。对于上面的代码,更好的写法是:

Person.number = 99;

【静态方法】:不需要通过实例变量来调用,可以直接通过类名调用

public class Main {
    public static void main(String[] args) {
        Person.setNumber(99);
        System.out.println(Person.number);
    }
}

class Person {
    public static int number;

    public static void setNumber(int value) {
        number = value;
    }
}

 和静态字段一样,属于类不是与实例,因此静态方法内部不可以调用this变量,也无法访问实例字段,只能访问静态字段。

通过实例变量也可以调用静态方法,但这只是编译器自动帮我们把实例改写成类名而已。

【接口静态字段】:接口里没有实例字段,但是可以有静态字段,并且必须为final类型

public interface Person {
    public static final int MALE = 1;  
 //实际上可以直接int MALE,因为编译器会自动帮我变为public static final
    public static final int FEMALE = 2;
}

【package】:在定义class时,在第一行声明这个class属于那个包,有点创建自己的包的意思

package ming; // 申明包名ming

public class Person {
}

【import】 :导包

【import static】:导入包中的静态字段,很少用

Java编译器最终编译出的.class文件只使用完整类名,因此,在代码中,当编译器遇到一个class名称时:

  • 如果是完整类名,就直接根据完整类名查找这个class

  • 如果是简单类名,按下面的顺序依次查找:

    • 查找当前package是否存在这个class

    • 查找import的包是否包含这个class

    • 查找java.lang包是否包含这个class

因此编写class时,编译器会自动帮我们import两个动作

  • import 当前package的其他class
  • import java,lang.*
// Main.java
package test;

import java.text.Format;

public class Main {
    public static void main(String[] args) {
        java.util.List list; // ok,使用完整类名 -> java.util.List
        Format format = null; // ok,使用import的类 -> java.text.Format
        String s = "hi"; // ok,使用java.lang包的String -> java.lang.String
        System.out.println(s); // ok,使用java.lang包的System -> java.lang.System
        MessageFormat mf = null; // 编译错误:无法找到MessageFormat: MessageFormat cannot be resolved to a type
    }
}

2021.8.24 

作用域

public class Main {
    public static void main(String[] args) {
        Outer outer = new Outer("Nested"); // 实例化一个Outer
        Outer.Inner inner = outer.new Inner(); // 实例化一个Inner
        inner.hello();
    }
}

【public】:可以被其他类访问,在不同的package之间访问需要public,同一个package就无所谓

【private】:只能在class内部访问

【protected】:允许子类访问

【package】:允许同一个包内,访问一个没有public,private修饰的类,以及没有public,private,protected修饰的字段和方法

  • 一个.java文件只能包含一个public类,但可以包含多个非public类。如果有public类,文件名必须和public类的名字相同。

内部类

class Outer {
    class Inner {
        // 定义了一个Inner Class
    }
}

【内部类】:不能直接实例化,要先实例一个Outer,在通过Outer new一个新的Inner,内部类可以调用Private,也可以用this

public class Main {
    public static void main(String[] args) {
        Outer outer = new Outer("Nested"); // 实例化一个Outer
        Outer.Inner inner = outer.new Inner(); // 实例化一个Inner
        inner.hello();
    }
}

 【匿名类】:待补充,暂时看不懂【unknown】

【静态内部类】:Static Nested Class是独立类,但只拥有Outer Class的private访问权限。

public class Main {
    public static void main(String[] args) {
        Outer.StaticNested sn = new Outer.StaticNested();
        sn.hello();
    }
}

class Outer {
    private static String NAME = "OUTER";

    private String name;

    Outer(String name) {
        this.name = name;
    }

    static class StaticNested {
        void hello() {
            System.out.println("Hello, " + Outer.NAME);
        }
    }
}

classpath和jar

【classpath】:环境变量的配置路径

【jar】:jar压缩包,包含很多class文件,方便下载和使用

模块

拓展知识,先跳过【unknow】


Java核心类

字符串和编码

【string操作】

string s1 = "Wudibooo";
string s2 = "WUDIBOOO" 

//比较  ,不要用==,因为string是引用类型不是基本数值类型
s1.equals(s2);
s1.equalsIgnoreCase(s2);

//包含
"Hello".contains("ll"); // true

//搜索子串
"Hello".indexOf("l"); // 2
"Hello".lastIndexOf("l"); // 3
"Hello".startsWith("He"); // true
"Hello".endsWith("lo"); // true

//substring
//提取子串 
"Hello".substring(2); // "llo"
"Hello".substring(2, 4); "ll"

//trim    strip(这个还可以移除中文空白字符)
//移除首尾的空白字符  \t \r \n  " "
"  \tHello\r\n ".trim(); // "Hello"
"\u3000Hello\u3000".strip(); // "Hello"

//replace
String s = "hello";
s.replace('l', 'w'); // "hewwo",所有字符'l'被替换为'w'
 
//分割split
String s = "A,B,C,D";
String[] ss = s.split("\\,"); // {"A", "B", "C", "D"}

//拼接join
String[] arr = {"A", "B", "C"};
String s = String.join("***", arr); // "A***B***C"

 【格式化字符串】:

常用占位符

  • %s:显示字符串;
  • %d:显示整数;
  • %x:显示十六进制整数;
  • %f:显示浮点数。
    public class Main {
        public static void main(String[] args) {
            String s = "Hi %s, your score is %d!";
            System.out.println(s.formatted("Alice", 80));
            System.out.println(String.format("Hi %s, your score is %.2f!", "Bob", 59.5));
        }
    }

【类型转换】

任何类型转string,用value0f()

String.valueOf(123); // "123"
String.valueOf(45.67); // "45.67"
String.valueOf(true); // "true"
String.valueOf(new Object()); // 类似java.lang.Object@636be97c

 string转其他型

//int
int n1 = Integer.parseInt("123"); // 123
int n2 = Integer.parseInt("ff", 16); // 按十六进制转换,255

//boolean
boolean b1 = Boolean.parseBoolean("true"); // true
boolean b2 = Boolean.parseBoolean("FALSE"); // false

 string和char互相转换

public class Main {
    public static void main(String[] args) {
        char[] cs = "Hello".toCharArray();
        String s = new String(cs);
        System.out.println(s);   //Hello
        cs[0] = 'X';
        System.out.println(s);  //Hello
    }  
}

需要注意的是,如果修改char[]的值,string并不会改变

如果要想通过修改char [] 来对string进行修改,可以使用下面的写法

public class Main {
    public static void main(String[] args) {
        int[] scores = new int[] { 88, 77, 51, 66 };
        Score s = new Score(scores);
        s.printScores();  //[88, 77, 51, 66]
        scores[2] = 99;
        s.printScores();  //[88, 77, 99, 66]
    }
}

class Score {
    private int[] scores;
    public Score(int[] scores) {
        this.scores = scores;
    }

    public void printScores() {
        System.out.println(Arrays.toString(scores));
    }
}

【转换编码】 :转换编码就是将Stringbyte[]转换,需要指定编码,转换为byte[]时,始终优先考虑UTF-8编码

2021.8.25

stringBuilder

对string做特殊处理时,可以是用 ‘ + ’号,但是每一次都会扔掉旧的字符串,造成内存浪费,所以java提供了stringBuilder,是一个可变的对象。

StringBuilder sb = new StringBuilder(1024);
for (int i = 0; i < 1000; i++) {
    sb.append(',');
    sb.append(i);
}
String s = sb.toString();

如果简单的 ‘ + ’,不需要stringBuildr,因为编译器会自动把 + 变回stringConcatFactory  操作

stringJoiner

【stringJoiner()】:拼接对象

public class Main {
    public static void main(String[] args) {
        String[] names = {"Bob", "Alice", "Grace"};
        var sj = new StringJoiner(", ", "Hello ", "!"); // 分隔号, 开头,结尾
        for (String name : names) {
            sj.add(name);
        }
        System.out.println(sj.toString());
    }
}

 【string.join()】:更方便,但是没有开头和结尾

包装类型

Java的数据分为,基本类型和引用类型。

想将基本类型变为一个应用类型,我们需要进行包装,将基本类型变为类里的一个静态字段。

public class Integer {
    private int value;

    public Integer(int value) {
        this.value = value;
    }

    public int intValue() {
        return this.value;
    }
}

java自己提供了很多包装类。

基本类型对应的引用类型
booleanjava.lang.Boolean
bytejava.lang.Byte
shortjava.lang.Short
intjava.lang.Integer
longjava.lang.Long
floatjava.lang.Float
doublejava.lang.Double
charjava.lang.Character

如何使用:

public class hellow {
    public static void main(String[] args) {
        int i = 100;
        Integer n1 = new Integer(i);  //不推荐使用NEW的方法
        Integer n2 = Integer.valueOf(i); //使用valueOf  里面可以放入int或string
        Integer n3 = Integer.valueOf("100");
    }
}

【自动装箱】:

int i = 100;
Integer n = Integer.valueOf(i);
int x = n.intValue();

 正常Interger 和int的转换是这样,实际我们可以省略,

Integer n = 100; // 编译器自动使用Integer.valueOf(int) ,自动装箱
int x = n; // 编译器自动使用Integer.intValue() ,自动拆箱

 【不变类】:源码使用的是private,不可以外部更改,而且不建议是用 ==  做比较。

【进制转换】:Interger内部提供了静态方法parseInt(),可以把String转INT

int n1 = Interger.parseInt("100");        //默认 10进制
int n1 = Interger.parseInt("100",16) //16进制

 也可以转制定进制的字符串

String s = Integer.toString(100)
String s1 = Integer.toHexString(100)

Number 所有的整数和浮点数的包装类型都继承自Number

// 向上转型为Number:
Number num = new Integer(999);
// 获取byte, int, long, float, double:
byte b = num.byteValue();
int n = num.intValue();
long ln = num.longValue();
float f = num.floatValue();
double d = num.doubleValue();

javaBean

是一种命名规范,通过getter和setter来定义属性,属性也只是一种叫法

    public String getName() { return this.name; } //getter
    public void setName(String name) { this.name = name; }  //setter

枚举类

【enum关键词】:enumerate,枚举

可以让编译器自动检查某个值在不在枚举的集合里,要注意的是enum对类型的非常严格,不同用途的枚举需要不同的类型来标记。

public class Main {
    public static void main(String[] args) {
        Weekday day = Weekday.SUN;
        if (day == Weekday.SAT || day == Weekday.SUN) {
            System.out.println("Work at home!");
        } else {
            System.out.println("Work at office!");
        }
    }
}

enum Weekday {  //enum常量本身带有类型信息,即Weekday.SUN类型是Weekday,类型不同会报错
    SUN, MON, TUE, WED, THU, FRI, SAT;
}

【emun比较】:这是因为enum类型的每个常量在JVM中只有一个唯一实例,所以可以直接用==

【emun特点】:

  • 定义的enum类型总是继承自java.lang.Enum,且无法被继承;
  • 只能定义出enum的实例,而无法通过new操作符创建enum的实例;
  • 定义的每个实例都是引用类型的唯一实例;
  • 可以将enum类型用于switch语句。
String s = Weekday.SUN.name(); // "SUN"  返回常量名
int n = Weekday.MON.ordinal(); // 1  返回定义的常量的顺序,从0开始计数

在ordinal()时,枚举集合里的顺序不小心修改了,就会导致ordinal的返回值也变化,这是不利的。通过给枚举常量添加字段,可以解决这问题

public class Main {
    public static void main(String[] args) {
        Weekday day = Weekday.SUN;
        if (day.dayValue == 6 || day.dayValue == 0) {
            System.out.println("Work at home!");
        } else {
            System.out.println("Work at office!");
        }
    }
}

enum Weekday {
    MON(1), TUE(2), WED(3), THU(4), FRI(5), SAT(6), SUN(0);

    public final int dayValue;

    private Weekday(int dayValue) {
        this.dayValue = dayValue;
    }
}

 还可以填加string,增加可读性

public class Main {
    public static void main(String[] args) {
        Weekday day = Weekday.SUN;
        if (day.dayValue == 6 || day.dayValue == 0) {
            System.out.println("Today is " + day + ". Work at home!");
        } else {
            System.out.println("Today is " + day + ". Work at office!");
        }
    }
}

enum Weekday {
    MON(1, "星期一"), TUE(2, "星期二"), WED(3, "星期三"), THU(4, "星期四"), FRI(5, "星期五"), SAT(6, "星期六"), SUN(0, "星期日");

    public final int dayValue;
    private final String chinese;

    private Weekday(int dayValue, String chinese) {
        this.dayValue = dayValue;
        this.chinese = chinese;
    }

    @Override
    public String toString() {
        return this.chinese;
    }
}

用在switch中 

public class hellow {
    public static void main(String[] args) {
        Weekday person = Weekday.boy;
        switch (person) {
            case boy:
                System.out.println("" + person);break;
            case girl:
                System.out.println("" + person); break;
            case man:
                System.out.println("" + person);break;
            case wuman:
                System.out.println("" + person);break;
        }
    }
}

enum Weekday{
    boy,girl,man,wuman;
}

记录类

【record类】:java14开始,用record关键词,可以一行写出一个不变类

public record Point(int x, int y) {}

等于,不仅定义了字段,还覆写了toString,equals,hashcode

public final class Point extends Record {
    private final int x;
    private final int y;

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

    public int x() {
        return this.x;
    }

    public int y() {
        return this.y;
    }

    public String toString() {
        return String.format("Point[x=%s, y=%s]", x, y);
    }

    public boolean equals(Object o) {
        ...
    }
    public int hashCode() {
        ...
    }
}

【record 里构造方法】: 自动创建的话,我们要想检查参数,要这样

public record Point(int x, int y)  {
    public Point(int x, int y) {   //称为Compact Constructor
        // 这是我们编写的Compact Constructor:
        if (x < 0 || y < 0) {
            throw new IllegalArgumentException();
        }
    }

    //也可以加上自己的静态方法
    public static Point fun() {
        return new Point(0, 0);
    }
}

var z = Point.fun();

 编译器会编译成 

public final class Point extends Record {
    public Point(int x, int y) {
        // 这是我们编写的Compact Constructor:
        if (x < 0 || y < 0) {
            throw new IllegalArgumentException();
        }
        // 这是编译器继续生成的赋值代码:
        this.x = x;
        this.y = y;
    }
    ...
}

【tips】

  • record定义的是不变类,在内部可以定义静态方法
  • 通过compact Constructor对参数进行验证 

BigInteger

数据大小不够用的时候,就建一个bigInteger

  • BigInteger用于表示任意大小的整数;
  • BigInteger是不变类,并且继承自Number
BigInteger bi = new BigInteger("1234567890");
System.out.println(bi.pow(5));
 // 2867971860299718107233761438093672048294900000

【类型转换】

BigInteger i = new BigInteger("123456789000");
System.out.println(i.longValue()); // 123456789000
  • 转换为bytebyteValue()
  • 转换为shortshortValue()
  • 转换为intintValue()
  • 转换为longlongValue()
  • 转换为floatfloatValue()
  • 转换为doubledoubleValue()

如需要精准的,可以用intValueExact(),longValueExact()

BigDecimal

类似BigInterger,同样继承自Number

【BigDecimal】

BigDecimal d1 = new BigDecimal("123.4567");

//用scale表示小数位
System.out.println(d1.scale()); // 4,4位小数
//如果返回的是负值,表示此数是整数,-2,就表示是整数,且末尾有2个零

 【BigDecimal.stripTrailingZeros()】:跳过末尾的zero

【四则运算】:+ - *没问题,除法在除不尽的时候,要设置保留的位数

BigDecimal d1 = new BigDecimal("123.456");
BigDecimal d2 = new BigDecimal("23.456789");
BigDecimal d3 = d1.divide(d2, 10, RoundingMode.HALF_UP); // 保留10位小数并四舍五入
BigDecimal d4 = d1.divide(d2); // 报错:ArithmeticException,因为除不尽

 【divideAndRemainder()】:返回商和余数,通常用于判断是否是某个数的整倍

BigDecimal n = new BigDecimal("12.75");
BigDecimal m = new BigDecimal("0.15");
BigDecimal[] dr = n.divideAndRemainder(m);
if (dr[1].signum() == 0) {
    // n是m的整数倍
}

 【比较】

  • 在使用equals时,要求数值和scale都要相等,所以不建议使用equals()
  • compareTo()

常用工具类

【Math】

  • abs
  • max   min 
  • pow 
  • sqrt
  • exp
  • log log10
  • sin cos tan asin acon
  • random     0-1

【Random】

在不给定种子的时候,是根据计算机的时间搓来计算的

Random r = new Random();
r.nextInt(); // 2071575453,每次都不一样
r.nextInt(10); // 5,生成一个[0,10)之间的int
r.nextLong(); // 8811649292570369305,每次都不一样
r.nextFloat(); // 0.54335...生成一个[0,1)之间的float
r.nextDouble(); // 0.3716...生成一个[0,1)之间的double

【SecureRandom】

生成安全的随机数,要用再看吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值