美文网首页
Java 学习基础篇 ---- Java面向对象编程

Java 学习基础篇 ---- Java面向对象编程

作者: 瀑月流光 | 来源:发表于2020-05-11 17:52 被阅读0次

    一、面向对象概念

    OOP:Object Oriented Programming class和instance是“模版”和“实例”的关系:
    类:class,对象模版
    实例:instance,具体对象 class用字段(field)封装了数据,不同的instance拥有各自独立的字段(field)
    通过变量.字段名访问某个字段(field)
    指向instance的变量都是引用变量

    二、数据封装

    一个 class 可以包含多个 field,直接把 field 用public暴露给外部可能破坏了封装,我们可以用 private关键字 修饰 field 进而拒绝外部访问。
    外部代码通过定义 public方法可以间接的修改 private关键字 修饰的 field。
    通过 变量.方法名() 来调用实例方法。
    通过 方法 访问实例字段更安全。

    public class Person{
        private String name;
        private int age;
        public String address;
    
        public void setName(String name){
            this.name = name;
        }
        public String getName() {
            return this.name; 
        }
    }
    
    Person ming = new Person();
    ming.address = "北京";     // 可以赋值
    ming.name = "小明";  // 编译错误
    ming.age = 12;   // 编译错误
    ming.setName(" 小明  ");
    System.out.println(ming.getName());   // "小明"
    

    (一)方法定义

    1、修饰符:public、private、
    2、方法返回值:有返回值的方法要写上返回值类型,没有返回值的方法 用void 来表示,void 表示不返回任何值(注意和返回null不同)
    3、方法名称:首字母小写,后面的单词首字母大写;
    4、方法参数:方法名称后的 () 内写入参数及参数类型列表,如果没有参数则空着,如果有多个参数则需要用逗号隔开每一个参数的类型及变量,方法参数可以是基本类型参数,也可以是引用类型参数。

    public class Person{
        private String name;
        private int age;
        public String address;
    
        public String getName() {
            return this.name; 
        }
    
        public void setName(String name){
            if (name == null){
                throw new NullPointException();
            }
            this.name = name.trim();
            return;   // 可以省略,因为 void 
        }
    
        public void retName(String name_1, String name_2){
            // Todo
        }
    }
    

    5、方法内部可以使用隐式变量 this,this指向当前实例,this.field 可以访问当前实例的字段。
    6、调用方法:实例变量.方法名(参数),可以忽略方法返回值。

    Person ming = new Person();
    ming.setName("  小明");   // 没有返回值
    String s = ming.getName();   // 返回值为 String
    

    7、可以定义 private 方法,外部代码不可以访问 private 方法,内部代码可以调用自己的 private 方法,外部代码可以通过 public 方法间接调用 private 方法。

    public class Person{
        private String name;
        private int age;
        public String getBirth() {
            retrun calcBirth(2016);
        }
        private int calcBirth (int currentYear){
            return currentYear - this.age;
        }
    }
    

    (二)构造方法

    1、构造方法可以在创建对象实例时初始化对象实例
    2、构造方法名就是类名
    3、构造方法的参数没有限制
    4、构造方法没有返回值,也没有 void
    5、必须使用 new操作符 调用构造方法

    public class Person{
        private String name;
        private int age;
        public Person(String name, int age) {      // 构造方法 Person
            this.name = name;
            this.age = age;
        }
    }
    

    6、如果一个类没有定义构造方法,编译器会自动生成一个默认的构造方法(无参数,无执行语句)

    public class Person{
        private String name;     // = null
        private int age;            // = 0
        private String address = "北京";
        public Person() {        // 编译器生成的默认构造方法
        }
    }
    

    7、如果自定义了构造方法,编译器就不再自动创建默认构造方法
    8、 构造方法参数的初始化顺序:先初始化字段,没有赋值的字段初始化为默认值(基本类型=0;引用类型=null),再执行构造方法的代码

    public class Person{
        private String name;     // = null
        private int age;            // = 0
        private String address = "北京";
        public Person(String name, int age, Sting address) {       // 三个参数,创建实例时传入
            this.name = name; 
            this.age = age;
            this.address = address;
        }
    }
    

    9、一个类的构造方法可以有多个,通过创建实例s时传入的参数个数、参数类型来判断具体调用了哪个构造方法

    public class Person{
        private String name;     // = null
        private int age;            // = 0
        private String address = "北京";
        public Person(String name, int age, Sting address) {       // 构造方法一
            this.name = name; 
            this.age = age;
            this.address = address;
        }
        public Person(String name){       // 构造方法二
            this.name = name; 
            System.out.println("hello world")
        }
        public Person(){             // 构造方法三
            System.out.println("hello")
        }
    }
    

    10、一个构造方法可以调用其他构造方法,便于代码复用,调用其他构造方法的语法是 this(..)

    public class Person{
        private String name;     // = null
        private int age;            // = 0
        private String address = "北京";
        public Person(String name, int age, Sting address) {      // 构造方法一
            this.name = name; 
            this.age = age;
            this.address = address;
        }
        public Person(String name){        // 构造方法二
            this(name,10,"北京")
        }
        public Person(){       // 构造方法三
            this("Andy")
        }
    }
    

    (三)方法重载

    1、方法重载入是指多个方法的方法名相同,但是各自的参数不同(参数个数,参数类型,参数位置),方法返回值类型通常是相同的。
    2、方法重载的目的是使相同功能的方法使用同一方法名,以便于调用

    public class Hello {
        public void indexOf (String name){
        }
        public void indexOf (int age){
        }
        public void indexOf (int age, String name){
        }
        public void indexOf (String name, int age){
        }
    }
    
    public class Main{
        public static void main(String[] agers){
            Hello s = new Hello();
            int n1 = s. indexOf('t');
            int n2 = s. indexOf('st', 20);
        }
    }
    

    三、继承多态

    (一)继承

    1、继承使用关键字 extends,子类 Student 获得了父类 Person 的所有功能,Student 只需要编写新增的功能。
    2、如果定义的类没有写 extends 关键字,则该类继承自 object 类。
    3、Java 只允许 class 继承自一个类,不与许同时继承自多个类,即一个类有且仅有一个父类。

    public class Person{
        private String name;
        private int age;
        publicString getName(){
            return name
        }
        public void setName(String name){
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void run(){
            System.out.println(name +  " is running!");
        }
    }
    
    public class Student extends Person {
        private int score;
        public void setScore(int score){
            return score;
        }
        public int getScore(){
            this.score = score;
        }
    }
    
    public class Main{
        public static void main(String[] args){
            Person p = new Person();
            Student s = new Student();
            p.setName("Xiao Ming");
            s.setName("Xiao Hong");
            p.run();     // Xiao Ming is running!
            s.run();     // Xiao Hong is running!
        }
    }
    

    4、Person 类定义的 private 字段无法被子类访问
    5、Person 类定义的 protected 修饰的字段可以被子类访问。即 protected关键字 把字段和方法的访问权限控制在了继承树内部。

    public class Person {
       private String name;
       private int age;
       protected String hight;
       public void run() {
       }
    }
    
    public class Student extends Person{
        public String hello(){
            return "Hello," + this. name;             // ERROR!
        }
        public String hello(){
            return "Hello," + this. hight;             // ok
        }
    }
    

    6、java 语言规定编写子类的构造方法时,必须在第一行调用父类的构造方法(使用 super() 语句调用),如果没有调用 super() 方法,编译器会默认的帮助我们调用 super() ; 但是如果父类没有默认的构造方法,即父类显式的实现了构造方法,则子类需要在自身的构造方法中显式(手动)调用 super() 方法,并传入父类需要的参数。实例如下:

    public class Person {
        public String name;
        public int age;
        public Person(String name){        // 显式的实现带一个参数的构造方法
            System.out.println("create Person");
        }
    }
    
    public class Student extends Person{
        public Student(){
            super("小红");         // 父类 Person 是显式构造的构造方法,所以子类 Student 需要显式调用 super(),并传入参数
            System.out.println(" create student");
        }
    }
    

    7、向上转型:可以对实例变量进行向上转型(upcasting),向上转型把子类型安全地变为更加抽象的类型。例如 Student 类实例对象 转型为 Person 类实例对象。

    Person p = new Person();
    Student s = new Student();
    Person ps = new Student();    // upcasting   ps 是 Person 类型
    Object o1 = p;    // upcasting
    Object o2 = s;    // upcasting
    

    8、向下转型:可以对实例变量进行向下转型(downcasting), 向下转型把抽象的类型变成一个具体的子类型。

    Person p = new Student()
    Student s = (Student) p;   // downcasting 
    

    向下转型很可能报错:ClassCastException

    Person p = new Person()
    Student s = (Student) p;   // ClassCastException
    

    9、instanceof 操作符:判断实例对象的类型

    Person p = new Person();
    System.out.println(p instanceof Person);      // true
    System.out.println(p instanceof Student);     // false
    

    (二)多态

    1、子类覆写父类的方法是覆写(Override),覆写的方法使用 @Override 修饰,该修饰非必须,但是可以让编译器帮助检查是否进行正确的覆写,如下:

    public class Person{
        public void run(){
        }
    }
    
    public class Student extends Person{
        @Override           // 可省略
        public void run(){
        }
        @Override           // Compile error!  因为没有进行正确覆写
        public void run( String name){
        }
    }
    
    Person p = new Student();         
    p.run();      
    

    2、方法签名如果不同就不是 Override,而是创建了一个新方法,如下:

    public class Person {
        public void run(){
        }
    }
    
    public class Student extends Person {         
        public void run(String s){           // 方法签名不同(有参数),所以不是覆写,而是创建了一个新的方法,此处省略了 @Override 关键字
        }
    }
    

    3、java 中引用变量的声明类型可能与其实际类型不符
    4、多态概念:实例对象的方法调用总是对应实际类型,即Java 的实例方法调用是基于运行时实际类型的动态调用
    5、多态定义:指针对某个类型的方法调用,其真正执行的方法取决于运行时实际类型的方法。所以我们对某个类型调用某个方法,执行的方法可能是某个子类的覆写方法。利用多态的允许添加更多类型的子类的特性,实现功能的扩展。

    public class Person{
        public void run(String name){
            System.out.println(name + " is running")
        }
    }
    
    public class Student extends Person{
        @Override           // 可省略
        public void run(String name){
            System.out.println("Student " + name + " is running")
        }
    }
    
    public class Main{
        public static void main(String[] args){
            Person p = new Person("Xiao Ming");
            Person s = new Student(("Xiao Hang");
            p.run();        // 调用的是 Person 类型的 run() 方法
            s.run();      // 调用的是实际类型 Student 的 run() 方法
        }
    }
    

    6、所有的类都是从 Object 类继承下来的,是在 Object 类的基础上使用多态实现了功能的扩展。例如:toString() 方法、equals() 方法、hashCode() 方法。

    public class Person{              // 在子类 Person 中对父类 Object 的各个方法进行覆写
        @Override
        public String toString(){         // toString() 方法:把接口 instance 输出为 String
        }
        @Override
        public boolean equals(Object o){       // equals() 方法: 判断两个接口 instance 是否逻辑相等
        }
        @Override
        public int hashCode(){    // hashCode() 方法:计算一个接口 instance 的哈希值
        }
    }
    

    7、super 方法可以调用父类的被覆写 Override 的方法,如下:

    public class Person{
        private String name;
        public String hello(){
            return "Hello, " + name;
        }
    }
    
    public Student extends Person{
        @Override
        public String hello(){
            return super.hello() + "!";          // 此处使用 super.hello() 调用父类的 hello() 方法,返回 “Hello,” + name 的执行结果
        }
    }
    

    8、 final 关键字:当我们定义一个类的方法时如果不希望该方法被子类覆写,可以使用 final 关键字修饰。
    9、使用 final 关键字修饰的类不能被继承;使用 final 关键字修饰的方法不能被Override;使用 final 关键字修饰的字段 field 在初始化后不能被修改;

    public class Person{
        public final void setName(String name){
        }
    }
    
    public final class Student extends Person{
        private final int score;
    }
    

    四、抽象类和接口

    (一) 抽象类

    1、抽象方法:如果一个class 定义了方法,但没有具体执行代码,这个方法就是抽象方法;
    (1)抽象方法使用 abstract 关键字修饰;
    (2)抽象方法没有任何执行语句;
    (3)因为无法执行抽象方法,因此实现抽象方法的类必须使用 abstract 关键字修饰为抽象类;
    (4)抽象类无法实例化,但可以被继承;
    (5)抽象类强迫其子类实现其定义的抽象方法,否则编译错误;

    public abstract class Person{
        public abstract void run();         // 定义一个抽象方法 run(),和一个抽象类 Person()
    }
    
    Person p = new Person();        // 编译错误, 因为抽象类无法实例化
    Person s = new Student();       // OK       Student类 继承了 Person类
    Person t = new Teacher();       // OK        Teacher类 继承了 Person类
    // 调用抽象方法实现多态:
    s.run();
    t.run();
    

    2、面向抽象编程:
    (1)不关心父类 Person类型 对象的具体类型
    (2)不关心新的子类是如何实现抽象方法 (run方法)的

    public abstract class Person{
        public abstract void run();
    }
    
    public class Student extends Person{
        @Override
        public voide run() {
        }
    }
    
    public class Teacher extends Person{ 
        @Override
        public void run() {
        }
    }
    
    Person s = new Student();
    Person t = new Teacher();
    s.run()
    t.run()
    

    3、总结:
    抽象方法定义了子类必须实现的接口规范
    定义了抽象方法的类就是抽象类
    从抽象类继承的子类必须实现抽象方法
    如果不实现抽象方法,则该子类仍是一个抽象类

    (二) 接口 --- Interface

    1、如果一个抽象类没有字段,所有方法全部是抽象方法,就可以把该抽象类改写为接口(interface),可以使用关键字 interface 来声明一个接口。

    public interface Person{
        // public abstract void run();              // 接口定义的方法默认是 public abstract (不需要写)
        void run();
    }
    

    2、interface 是 Java 内置的纯抽象接口,实现 interface 使用 implements。

    public class Student implements Person{
        @Override
        public void run(){}
    }
    

    3、一个类可以同时实现多个 interface。

    public class Robot implements Hello, Comparable{
    }
    

    4、一个类如果想实现某个接口,子类必须实现该接口中所定义的全部方法,如果接口中方法是使用 default 关键字定义的,则该方法默认被子类实现了

    // 接口
    public interface Shape{
        double area();              //  继承时 area方法 需要显式实现
        default double perimeter(){              // default 关键字修饰的方法,继承时默认实现
            return 0;
        }
    }
    
    // 实现接口的子类
    public class Rect implements Shape{
        private final double width;
        private final double height;
        public Rect(double width, double height){
            this.width = width;
            this.height = height;
        }
        @Override
        public double area(){
            return width * height
        }
    }
    
    public class Main{
        public static void main(String[] args){
            Shape s1 = new Rect(200, 100);
            System.out.println(s1.area());
        }
    }
    

    5、一个 interface 可以继承自另一个 interface,interface 之间的继承使用 extends 关键字

    public interface Person {
        String getName();
    }
    
    public interface Student extends Person{
        String getSchool();
    }
    
    public class PrimaryStudent implements Student{
        @Override
        public String getName(){
        }
        @Override
        public String getSchool(){
        }
    }
    

    6、interface 的继承相当于扩展了 interface(接口)的方法

    (三) 抽象类 vs 接口

    1、合理设计 interface 和 abstract class 的继承关系:公共逻辑放在 abstract class 中。
    2、类可以实现多个接口。
    3、接口也是数据类型,适用于向上转型和向下转型。
    4、接口不能定义实例字段。
    5、接口可以定义 default 方法(JDK >= 1.8)

    五、包和classpath

    (一) 静态字段和静态方法

    1、用 static 修饰的字段称为静态字段,普通的字段在每个实例中都有自己的一个独立"空间",静态字段只有一个共享"空间"。
    2、对于一个类的所有实例,都共享一个静态字段。
    3、不推荐用实例变量访问静态字段。
    4、推荐用类名访问静态字段。
    5、可以把静态字段理解为描述 class 本身的字段(非实例字段)。

    public class Person{
        public String name;
        public int age;
        public static int number = 100;       // 定义一个静态字段 number
    }
    
    Person ming = new Person();
    Person hong = new Person();
    ming.number = 99;            // 通过 实例变量 访问静态字段,不推荐
    System.out.println(hong.number);       // 99
    Person.number = 88;                     // 使用 类名 调用静态变量 
    System.out.println(Person.number);    // 88     
    

    6、用 static 修饰的方法称为静态方法,调用实例方法必须通过实例变量,调用静态方法不需要实例变量,可直接使用类名调用(静态方法类似于其他编程语言中的函数)。

    public class Person{
        private String name;
        private static int number = 100;
        pulic static viod setNumber(int num){
            number = num;
        } 
    }
    
    Person.setNumber(99);
    

    7、静态方法不能访问 this 变量。
    8、静态方法不能访问实例字段。
    9、静态方法可以访问静态字段、及其他静态方法。
    10、Java程序的入口 main() 也是静态方法。

    (二) 包

    1、Java 定义了名字空间:包,包名 + 类名 = 完整类名。
    2、包(package) 可以是多层结构:java.util.Arrays。
    3、包没有父类 / 子类关系:java.util 和 java.util.zip 是不同的包,两者没有任何关系。
    4、包作用域:不用 public、protected、private修饰的字段和方法就是包作用域。
    5、位于同一个包的类,可以访问包作用域的字段和方法。

    package hello;
    public class World{
        static int findMax(int[] ns){
        }
    }
    
    package hello;
    public class Map{
        int price;
        void setPrice(int price)
    }
    

    6、包的引用:

    package hello;
    import java.util.Arrays;
    
    public class World{
        public int findMin(int[] ns){
            Arrays sort(ns);
            return ns[0];
        }
    }
    

    (三) 作用域

    1、访问权限是指在一个类的内部,能否引用另一个类以及访问它的字段和方法。
    2、访问权限有 public、protected、private 和 package 四种。
    (1)定义为 public 的 field、method 可以被其他类访问;
    (2)定义为 private 的 field、method 无法被其他类访问,但是在类的内部可以访问(即 private 访问权限限定在了 class 的内部);
    (3)private 还可以修饰 class,定义为 private 的 class 无法被其他类访问
    (4)访问 private class 被限定在外层 class 的内部,定义在一个 class 内部的 class 称为 内部类(inner class)。

    package abc;
    
    public class Hello{
        public void hello(){
            Inner inner = null;
        }
        private class Inner{         // 定义一个内部类   Inner
        }
    }
    

    (5)protected 作用于继承关系,定义为 protected 的字段和方法可以被子类访问

    package abc;
    
    public class Hello{
        protected void hi(){
        }
    }
    
    package xyz;
    
    class World extends Hello {
         void foo(){
            Hello h = new Hello();
            h.hi();
        }
    }
    

    (6)包作用域(package) 是指一个类允许访问同一个 package 的没有 public、private 修饰的 class,以及没有 public、protected、private 修饰的 字段和方法。注意:包名必须完全一致

    package abc;
    
    class Hello {
        void hi() {
        }
    }
    
    package abc;
    
    class Main {
        void foo()[
            Hello h = new Hello();
            h.hi()
        }
    }
    

    3、最佳实践:最小化暴露对外方法,即尽量少使用 public 。
    4、局部变量:在方法内部定义的变量称为局部变量,局部变量作用域由所在语句块{...}决定。

    package abc;
    
    public class Hello{
        void hi(String name){
            String s = name.toLowerCase();
            int len = s.length();
            if (len < 10){
               int p = 10 - len;
               for (int i = 0; i < 10; i++){
                   System.out.println(i);
               }
            }
        }
    }
    

    5、final 关键字:
    (1)final 与访问权限不冲突;
    (2)用 final 修饰 class 可以阻止被继承;
    (3)用 final 修饰 method 可以阻止被覆写;
    (4)用 final 修饰 field 可以阻止被重新赋值;
    (5)用 final 修饰局部变量可以阻止被重新赋值;

    package abc;
    
    public class Hello{
        private int n = 0;
        protected void hi(final int t){
            final long i = t;
        }
    }
    

    6、一个.java 文件只能包含一个 public class, 但是可以包含多个非 public class。

    (四) classpath 和 jar

    1、jar 包是 zip 格式的压缩文件,包含若干 .class 文件。
    2、jar 包相当于目录,classpath 可以包含 jar 文件,例如:C:\work\bin\all.jar。
    3、查找 com.fiya.Hello 类会在 C:\work\bin\all.jar 文件中搜索 com/fiya/Hello.class。
    4、jar 包的其他功能:
    (1)jar 包可以包含一个特殊的 /META-INF/MANIFEST.MF 文件,该文件是纯文本,可以指定 Main-Class 和其他信息。
    (2)jar 包还可以包含其他 jar 包。
    5、JVM运行时会自动加载 JDK 自带的 class。JDK自带的 class 被打包在 rt.jar 中。

    六、Java核心类

    (一) 字符串和编码

    1、String 可以使用 "" 来直接创建,创建后内容不可变。

    String s1 = "hello";        // 创建 String 方法一
    String s2 = new String("world");        // 创建 String 方法二
    

    2、字符串常用操作:
    (1)使用 equals() 判断字符串是否相等,忽略大小写 equalsIgnoreCase(String)

    String s = "hello";
    s.equals("Hello");    // false
    s.equalsIgnoreCase("Hello")     //  true
    

    (2)是否包含字符串:boolean contains(CharSequence)

    String s = "hello world";
    s.contains("ll");   // true
    

    (3)查找字符串位置:int indexof(String) 和 int lastIndexof(String)

    String s = "hello world";
    s.indexof("ll");     // 从前向后查找  2
    s.lastIndexof("l")      // 从后向前查找  9
    

    (4)是否以指定字符串开头:boolean startsWith(String)

    String s = "hello world";
    s.startsWith("he");         // true
    

    (5)是否以指定字符串结尾:boolean endsWith(String)

    String s = "hello";
    s.endsWith("lo");       // true
    

    (6)移除字符串首尾空白字符:trim(),注意 trim() 不改变字符串内容,而是返回新的字符串。

    String s = " \t hello \r\n ";
    String s2 = s.trim();     // "hello"
    s = s.trim();
    

    (7)提取字符串子串:substring()

    String s = "hello,world";
    s.substring(7);      // "world"
    s.substring(1, 5);       // "ello"
    

    (8)大小写转换:toUpperCase()、toLowerCase()

    String s = "heLLo";
    s.toUpperCase();        // "HELLO"
    s.toLowerCase();        // "hello"
    

    (9)替换子串普通替换:replace(char, char)

    String s = "hello";
    s.replace('l', 'w');      // "hewwo"
    s.replace('l', 'w~');     // "hew~w~o"
    

    (10)替换子串正则表达式替换:replaceAll(String,String)

    String s = "A,,B;C ,D";
    s.replaceAll("[\\,\\;\\s]+", ", ");         // "A, B, C, D"
    

    (11)分割字符串:String[] split(String)

    String s = "A,,B;C ,D";
    String[] ss = s.split("[\\,\\;\\s]+");           // {"A", "B", "C", "D"}
    

    (12)拼接字符串:static String join()

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

    3、String 类型和 其他任意类型 的转换
    (1)把任意数据转换为 String 类型:static String valuOf(int)、static String valuOf(boolean)、static String valuOf(Object)

    String.valueOf(123);     // "123"
    String.valueOf(true);     // "true"
    String.valueOf(new Object());   // "java.lang.Object@7852e922"
    

    (2)把 String 类型转换为其它类型:
    static int Interger.parseInt(String)

    int i = Integer.parseInt("123");      //123
    

    static Interger Interger.valueOf(String)

    Integer I = Interger.valueOf("123");
    

    4、String 与 char[] 互相转换:
    (1)String 转换为 char[]:char[] toCharArray();

    String s = "hello";
    char[] cs = s.toCharArray();            // {'h', 'e', 'l', 'l', 'o'}
    

    (2)char[] 转换为 String :new String(char[]);

    char[] cs = {'h', 'e', 'l', 'l', 'o'};
    String s2 = new String(cs);           // "hello"
    

    5、String和byte[]互相转换(需要指定编码)
    (1)String 转换为 byte[]: byte[] getBytes(String)、byte[] getBytes(Charset)

    String s = "hello";
    byte[] bs1 = s.getBytes("UTF-8");
    byte[] bs2 = s.getBytes(StandardCharsets.UTF_8);
    

    (2)byte[] 转换为 String: new String(byte[], String)、new String(byte[], Charset)

    new String(bs1, "UTF-8");
    new String(bs2, StandardCharsets.UTF_8);
    

    6、一个中文字符占用2个字节,中文编码:GB31、GBK、GB8030.
    7、全球统一编码:Unicode。Java使用的就是 Unicode 编码。

    (二) StringBuidler

    1、StringBuilder 是可变对象,可以预分配缓冲区,进而实现高效的拼接字符串。

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

    2、StringBuilder 可以进行链式操作。

    StringBuilder sb = new Stringbuilder(1024);
    String s = sb.append("Ms ").append("name").append("!").insert(0, "Hello, ").toString();
    

    3、编译器在内部自动把多个连续的 + 号操作优化为 StringBuilder 操作。

    String s = "Hello, " + "nemw" + "!";
    

    4、Java 还提供了一个和 StringBuilder 接口完全相同的接口 StringBuffer,StringBuffer 是线程安全的,一般没必要使用。

    (三) 包装类型

    1、Java 的属性包括 基本数据类型 和 引用数据类型,基本类型不能视为对象,如果我们把一个基本类型赋值为null ,程序会报错。

    int n = null;     // ERROR!   
    

    2、可以对基本类型进行包装,变成一个引用类型(即包装类型实际上是一个引用类型,只不过其内部包含一个基本类型的变量)。

    // 定义一个 Integer 类,包含一个实例字段 int
    public class Integer {
        private int value;
        public Integer(int value) {
            this.value = value;
        }
    }
    Integer n = null;
    Integer n2 = new Integer(99);
    

    以上是我们手动实现的包装类型,其实 JDK 已经为每种基本类型都创建了对应的包装类型,无需我们手动实现:

    基本类型                对应的引用类型
    boolean                  Boolean
    byte                        Byte
    short                      Short
    int                          Integer
    long                       Long
    float                      Float
    double                  Double
    char                      Character
    

    3、基本类型int 和 对应的引用类型Integer 可以相互转化:

    Integer n = new Integer(99);           // 基本类型 转换为 引用类型
    int i = n.intValue();                 // 引用类型 转换为 基本类型
    Integer n1 = Interger.valueOf("88");      // String类型 转换为 引用类型Interger
    String s = n.toString();          // 引用类型Interger   转换为   String类型
    

    特别注意 Integer.getInteger(String) 是从系统环境中读取系统变量。
    4、编译器可以自动在 int 和 Integer 之间转型:自动装箱、自动拆箱

    Integer n = 99;            // Integer.valueOf(99)        自动装箱 int -> Integer
    int i = n;          // n.intValue()          自动拆箱 Integer -> int
    

    注意:自动装箱和自动拆箱只发生在编译阶段;
    装箱和拆箱会影响执行效率;
    编译后的class代码是严格区分基本类型和引用类型的;
    自动拆箱时:Integer -> int 执行时可能会报错(null 时),即 Integer x = null; int y = x; // NullPointerException
    5、Java 的包装类型定义了一些有用的静态变量

    Boolean t = Boolean.TRUE;
    Boolean f = Boolean.FALSE;
    int max = Integer.MAX_VALUE;       // 2147483647
    int min = Integer.MIN_VALUE;       // -2147483647
    int sizeOfLong = Long.SIZE;        // 64 (bite)
    int bytesOfLong = Long.BYTES;       // 8(bytes)
    

    6、整数和浮点数包装类型继承自 Number,所以我们可以对这些包装类型进行向上转型(变为 Number 类型),然后使用 Number 类型的一些方法将其转化为任意基本类型:


    继承关系.png
    Number num = new Integer(999)         // 将包装类型 Integer 向上转型为 Number 类型
    byte b = num.byteValue();
    int n = num.intValue();
    long ln = num.longValue();
    float f = num.floatValue();
    double d = num.doubleValue();
    

    (四) JavaBean

    1、许多 class 的定义都符合:a.若干 private 实例字段、b.通过 public 方法读写实例字段。符合以上两种命名规范的 class 被称为 JavaBean,即:
    (1)private Type field
    (2)public Type getField()
    (3)public void setField(Type value)
    注意:以上三点中方法名称大小写不能改变。

    public class Person{
        private String name;
        private int age;
    
        public String getName(){
        }
        public void setName(String name){
        }
    
        public int getAge(){
        }
        public void setAge(int age){
        }
    }
    

    boolean 字段的读方法命名为 isXxx():

    private boolean child
    public boolean isChild() {}     // 读方法
    public void setChild(boolean value){}
    

    2、通常把一组对应的 getter 和 setter 方法称为属性(Property):
    (1) 读写属性都具备:
    name 属性:
    有对应读方法 getName()
    有对应写方法 setName()

    (2) 只有 setter 的属性称为只写属性(Write-only):
    age 属性:
    无对应读方法 getAge()
    有对应写方法 setAge()

    (3) 只有 getter 的属性称为只读属性(Read-only):
    age 属性:
    有对应读方法 getAge()
    无对应写方法 setAge()

    (五) 枚举类型

    1、常量的定义
    (1)可以使用 enum 关键字定义常量, 使用 enum 关键字定义的常量本身带有类型,使用 == 比较常量是否相等。
    (2)enum 可以定义常量类型,它被编译器编译为:final class Xxx extends Enum{...}

    public enum Weekday{                // 定义了一些 Weekday 类型的常量
        SUN, MON, TUE, WED;
    }
    public enum Color {          // 定义了一些 Color 类的常量
        RED, GREEN, BLUE;
    }
    if (day == Weekday.FRI){
    }
    

    enum 定义的类型实际上是 class,继承自 java.lang.Enum(如下),不能通过 new 创建实例,所有常量都是唯一实例,可以用于 switch 语句

    public enum Color{
        RED, GREEN, BLUE;
    }
    // 编译器编译出来的 class:
    import java.lang.Enum
    public final class Color extends Enum{
        public static final Color RED = new Color();
        public static final Color GREEN = new Color();
        public static final Color BLUE = new Color();
        private Color(){}
    }
    

    2、常量的一些使用:
    (1)name() 获取常量定义的字符串,注意不要使用 toString()
    (2)ordinal() 返回常量定义的顺序(无实质意义)

    package com.test;
    public enum Weekday {
        SUN, MON, TUE, WED, THU, FRI, SAT;
    }
    
    package com.test;
    public class Main{
        public static void main(String[] args){
            for (Weekday day : Weekday.values()){
                System.out.println(day.name());
            }
            Weekday fri = Weekday.FRI;      // 将常量 FRI 赋值给 fri
            System.out.println(fri.name());       // enum类型 -> String类型
            System.out.println(fri.ordinal());      // 返回常量定义的顺序
            System.out.println(Weekday.valueOf("FRI").name());      // String类型 —> enum类型
            Weekday.valueOf("ABC");           // 不存在的常量,会抛出 IllegalArgumentException
        } 
    }
    

    (3)构造方法申明为 private
    (4)可以为enum枚举类编写构造方法、字段和方法,例如为每个枚举类型添加一个中文名称:

    package com.test;
    public enum Weekday {
        // b. 传入构造方法的参数,继而调用构造方法
        SUN("星期日"), MON("星期一"), TUE("星期二"), WED("星期三"), THU("星期四"), FRI("星期五"), SAT("星期六");        
        private String chinese;
        private Weekday(String chin){         // a. 使用构造方法 初始化 chinese 
            this.chinese = chin;
        }
        public String toChinese() {         // c. 定义 toChinese 方法返回  chinese
            return chinese
        }
    }
    
    public class Main{
        public static void main(String[] args){
            Weekday fri = Weekday.FRI;
            System.out.println(fri.toChinese());           // 星期五
        }
    }
    

    (六) 常用工具类

    1、Math 类:提供类数学计算的静态方法:
    (1)abc / min / max
    (2)pow / sqrt / exp / log / log10
    (3)sin / cos / tan / asin / acos
    (4)常量:PI = 3.14159;E = 2.71828
    (5)Math.random() 生成一个随机数,范围 0 <= 随机数 < 1

    double x1 = Math.random();
    long MIN = 1000;
    long MAX = 9000;
    double x2 = Math.random() * (MAX - MIN) + MIN;
    double r = (long) x2;
    

    (6)Random 对象:Random 对象用来创建伪随机数

    Random r = new Random();
    r.nextInt();       // 生成下一个随机 int
    r.nextLong();    // 生成下一个随机 long
    r.nextFloat();    // 生成下一个随机 float,介于 0~1
    r.nextDouble();    // 生成下一个随机 double,介于 0~1
    r.nextInt(10);     // 生成 0~10 之间的随机数,不包括 10
    

    (7)为随机数:给定种子后伪随机数算法会生成完全相同的序列,不给定种子时 Random 使用系统当前时间戳作为种子。

    Random r = new Random(12345);
    for (int i = 0; i < 10; i ++){
        System.out.println(r.nextInt(100))
    }
    

    2、SecureRandom:生成安全的随机数。
    3、BigInteger:表示任意大小的整数。
    4、BigDecimal:表示任意精度的浮点数。
    5、BigInteger 和 BigDecimal 都继承自 Number。

    相关文章

      网友评论

          本文标题:Java 学习基础篇 ---- Java面向对象编程

          本文链接:https://www.haomeiwen.com/subject/duavwhtx.html