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

面向对象编程

基础篇

方法

  • 每个类中含有多个域,但我们将域设置为public时,域就会暴露给外部从而破坏封装性,从而造成逻辑混乱。为了避免外部代码直接访问类的域,我们将其设置为private,此时要想访问域,则需要通过方法来操作;

    public class Main {
        public static void main(String[] args) {
            Person k = new Person();
            k.setName("Cunyu"); // 设置name
            k.setAge(25); // 设置age
            System.out.println(k.getName() + ", " + k.getAge());
        }
    }
    
    class Person {
        // 直接设置为public,然后直接操作域,则会编译报错
        // public String name;
        // public int age;
        
        // 设置为private,然后通过方法访问
        private String name;
        private int age;
    
        public String getName() {
            return this.name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return this.age;
        }
    
        public void setAge(int age) {
            if (age < 0 || age > 100) {
                throw new IllegalArgumentException("invalid age value");
            }
            this.age = age;
        }
    }
    
    
  • 定义方法的语法如下,需要注意的是定义private方法是方便内部方法调用

    修饰符 方法返回类型 方法名(方法参数列表){
        // 实现方法功能的语句
        ...
        return 方法返回值;
    }
    
  • this变量

    方法内部使用隐含变量this,它始终指向当前实例,通过this访问当前实例的字段从而避免命名冲突;

  • 方法参数

    方法可以有0或任意个参数,用于传递给方法变量值,通过调用方法,按照其定义严格传递;

  • 可变参数

    通过类型 ...定义,相当于数组类型

    class Group{
    	private String[] names;
    	
    	public void setName(String... names){
        // 等价于
        // public void setName(String[] names){
    		this.names = names;
    	}
    }
    
  • 参数绑定

    • 使用基本数据类型做方法形参时,在方法体中对形参进行修改不会影响实参数值;
    • 使用引用数据类型作方法形参时,若在方法体中修改形参指向数据内容对实参变量数值产生影响,因为形参和实参指向同一对象;
    • 使用引用数据类型作方法形参时,若在方法体中修改形参的指向不会对实参变量数值产生影响,因为形参和实参指向不同对象;

构造方法

在进行初始化对象实例时,为了将其内部字段初始化,这时则需要构造方法来初始化实例。构造方法的名称是类名,其参数也没有限制,但是没有返回值,调用时必须使用new操作符;其次,构造方法中引用类型字段都默认为nullint默认为0,布尔类型默认为false;构造方法之间也可以相互调用

方法重载

当在一个类中需要定义多个功能类似的方法,但其参数不同,则将这一系列方法叫做同名方法,这种方法名相同,但各自参数不同称为方法重载(overload),其返回值类型一般相同

class Demo{
    public void hello(){
        System.out.println("hello, Java!");
    }
    
    public void hello(String name){
        System.out.println("hello, " + name + "!");
    }
}

继承

  • 为了提高代码复用率,常利用继承从父类获得所有功能,通过extends关键字来实现继承;

    class Person {
        private String name;
        private int age;
    
        public String getName() {...}
        public void setName(String name) {...}
        public int getAge() {...}
        public void setAge(int age) {...}
    }
    
    class Student extends Person {
        // 不要重复name和age字段/方法,
        // 只需要定义新增score字段/方法:
        private int score;
    
        public int getScore() {}
        public void setScore(int score) {}
    }
    
  • 继承树

    Java中所有类均继承自Object,只允许单继承,即一个类有且只有一个父类,而Object无父类;

  • protected

    子类无法访问父类private修饰的字段或方法,从而使得继承作用大大削弱,为了解决这个问题,可以将父类中的private改为protected,方便子类使用;

  • super

    super表示父类,子类引用父类字段时,可以使用super.fieldName,子类调用父类构造方法是,必须显式调用super(),子类不会继承任何父类构造方法;

    class Student extends Person {
        public String hello() {
            return "Hello, " + super.name;
        }
    }
    
  • 向上&下转型

    向上转型即将子类安全转换为父类类型的赋值,而向下转型则是将父类类型强制转换为子类类型,向下转型时最好利用instanceof先进行判断

  • 继承和组合

    继承是is关系,而组合则是has关系;

多态

继承关系中,若子类定义了与父类方法签名完全相同的方法,则被称为重写(Override)

**PS:**方法名相同,方法参数相同,方法返回值不同,也属于不同方法;

重写和重载的区别:若方法签名不同,则是重载,重载是一个新的方法;若方法签名相同且返回值也相同,则是重写

多态:针对某类型的方法调用,真正执行的方法却决议运行时实际类型的方法;

final:继承允许子类重写父类方法,若父类不允许子类对某一方法进行重写,则课将该方法标记为finalfinal修饰的方法无法被重写;

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

Student extends Person {
    // compile error: 不允许重写
    @Override
    public String hello() {
    }
}

抽象类

将方法声明abstract,则表示它是一个抽象方法,本身无具体执行代码,此时必须将包含抽象方法的类也声明为abstract,值得注意的是:抽象类无法实例化

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

abstract class Person{
    public abstract void run();
}

class Student extends Person{
    @Override
    public void run(){
        System.out.println("Student.run");
    }
}
  • 面向抽象编程的本质
    • 上层代码只定义规范;
    • 无需子类即可正常编译;
    • 方法的具体实现由不同子类实现,调用者不用关心;
  • 总结
    • abstract定义的方法只是抽象方法,只有定义,没有实现,此外,抽象方法还定义了子类必须实现的接口规范;
    • 从抽象类继承来的子类必须实现抽象方法;
    • 若不实现抽象方法,则该子类仍是抽象类;

接口

  • 抽象方法的本质是定义接口规范,但若一个抽象类中无字段而只有抽象方法,则可以将该类改写为接口interface,具体的类要实现接口时,使用关键字implements

    interface Person{
        void run();
        String getName();
    }
    
    class Student implement Person{
        private String name;
        
        public Student(String name){
            this.name = name;
        }
        
        @Override
        public void run(){
            System.out.println(this.name + "run");
        }
        
        @Override
        public String getName(){
            return this.name;
        }
    }
    
  • Java中,每个类只能继承一个类,无法从多个类继承,但一个类却可以实现多个interface

  • 接口之间也可以相互继承,接口之间继承使用extends关键字;

    interface Demo{
        void hello();
    }
    
    interface Demo1 extends Demo{
        void run();
        String getName();
    }
    
  • 继承关系

抽象类和接口对比

abstract class interface
继承 只能extends一个class implements多个interface
字段 可以定义实例字段 不能定义实例字段
抽象方法 可以定义抽象方法 可定义抽象方法
非抽象方法 可定义非抽象方法 可定义default方法

静态字段和方法

  • 类中定义的字段称为实例字段,其特点是每个实例均有独立字段,各实例的同名字段互不影响。实例字段在每个实例中具有自己的独立“空间”;

  • static修饰的字段叫做静态字段,静态字段只有一个共享“空间”,所有实例均共享该字段;

    class Person{
        // 实例字段
        public String name;
        public int age;
        // 静态字段
        public static int number;
    }
    
  • 不推荐用实例变量.静态字段去访问静态字段,因为Java中实例对象无静态字段,实际上能访问是因为编译器可以根据实例类型自动转换为类名.静态字段来访问静态对象;

  • 静态方法:static修饰的方法,调用实例方法需要实例变量,但调用静态方法不需要实例变量,只需要通过类名即可调用;静态方法不属于实例,因此在静态方法内部无法访问this变量,同时也无法访问实例字段,只能访问静态字段;

    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 = nvalue;
        }
    }
    
  • 接口的静态字段

    interface属于纯抽象类,所以无法定义实例字段,但是可以有静态字段,且接口中的静态字段必须为final类型;

    public interface Person{
        public static final int MALE = 2;
        public static final int FEMALE = 1;
    }
    

  • 一个类总是属于一个包:package,类名只是简写,完整的类名应该是包名.类名,包属于多层结构,不同层之间用.隔开;而且包之间不存在父子关系;

  • 当需要引用其他类时,通常有三种写法:

    • 直接写出完整类名;
    • 通过import语句;
    • import static,导入一个类的静态字段和方法;
    package ming;
    
    // 完整类名
    public class Person{
        public void run(){
            mr.jun.Arrays arrays = new mr.jun.Arrays();
        }
    }
    
    package ming;
    
    import mr.jun.Arrays;
    
    // import
    public class Person{
        public void run(){
            Arrays arrays = new Arrays();
        }
    }
    
    package ming;
    
    import static java.lang.System.*;
    // import static
    
    public class Person{
        public void main(String[] args){
            out.println("Hello, Java");
        }
    }
    
  • 类的查找顺序

    • 若是完整类目,直接查找对应类;
    • 若是简单类名,先查找当前package是否存在这个类;
    • 若是简单类名,查找import的包是否包含此类;
    • 若是简单类名,查找java.lang包是否包含此类;
  • 创建新类时,默认的import动作

    • 默认import当前package的其他class
    • 默认import java.lang.*

作用域

作用域 当期类 同一package 子孙类 其他package
public
protected
private
friendly
  • 局部变量:方法内部定义的变量,局部变量作用域从变量声明处开始到对应的块结束,注意方法参数也属于局部变量;

classpath和jar

  • classpathJVM用到的环境变量,用于指示JVM如何搜索class

模块

Java核心类

字符串及编码

  • String

    字符串在String内部通过char[]数组实现,最重要的一个特点是不可变

    public class Main{
        public static void main(String[] args){
            String s1 = "hello";
            String s2 = "hello"// 字符串比较
            if(s1.equals(s2)){
                System.out.println("s1 == s2");
            }
            
            // 去除首尾空白字符
            System。out.println("   helloJava  ".trim());
            
            // 替换子串
            String s3 = "hello";
            s.replace('o', 'w');
            
            // 字符串分割
            String s4 = "A,B,C,D";
            String[] ss = s4.split("\\,");
            
            // 字符串拼接
            String[] arr = {"a", "b", "c"};
            String s5 = String.join("**", arr);
            
            // 其他类型转换为字符串
            String.valueOf(123);
            String.valueOf(true);
            
            // String和char[]相互转换,此时改变char[]数组时,不会改变String
            char[] chArray = "hello".toCharArray();
            String s6 = new String(chArray);
        }
    }
    
  • 字符编码

    Java使用Unicode编码表示Stringchar;编码转换是将Stringbyte[]进行转换,转换时需要指定编码;

StringBuilder

String会在每次循环过程中创建新的字符串对象,然后扔掉旧字符串,造成内存浪费。为此,提供StringBuilder,这是个可变对象,可以预分配缓冲区,对它进行操作时不会创建新的临时对象,同时也支持链式操作;

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

String s = sb.toString();

StringJoiner

包装类型

基本类型 对应的引用类型
boolean java.lang.Boolean
byte java.lang.Byte
short java.lang.Short
int java.lang.Integer
long java.lang.Long
float java.lang.Float
double java.lang.Double
char java.lang.Character
  • 自动装/拆箱(Auto Boxing/Unboxing)

    自动装箱是指将基本类型变为对应引用类型,自动拆箱是指将对应的引用类型转换为基本类型

JavaBean

  • 通常将一组对应的读方法(getter)和写方法(setter)称之为属性(property),需要注意的是boolean字段的读方法一般命名为isXyz(),具有上述熟悉的类叫做JavaBean

    // 一般类型
    // 读方法
    public Type getXyz()
        
    // 写方法
    public void setXyz(Type value)
    
  • 作用

    用于传递数据,即将一组数据组合为一个JavaBean便于传输。此外也方便IDE进行分析,生成读写属性的代码,主要用于图形界面可视化设计;

  • 枚举JavaBean属性

    当需要枚举一个 JavaBean的所有属性时,可利用核心库提供的Introspector,利用Introspector.getBeanInfo()获取属性列表;

    public class Main {
        public static void main(String[] args) throws Exception {
            BeanInfo info = Introspector.getBeanInfo(Person.class);
            for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
                System.out.println(pd.getName());
                System.out.println("  " + pd.getReadMethod());
                System.out.println("  " + pd.getWriteMethod());
            }
        }
    }
    
    class Person {
        private String name;
        private int age;
    
        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;
        }
    }
    

枚举类

  • 为了编译器能自动检查某一值在枚举的集合内,且不同用途的枚举用不同类型来标记,使用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 {
        SUN, MON, TUE, WED, THU, FRI, SAT;
    }
    
  • enum类型的特点

    • 定义的enum类型继承自java.lang.Enum,且无法被继承;
    • 只能定义出enum的实例,且无法通过new操作符来创建新实例;
    • 定义的每个实例都是引用类型的唯一实例;
    • enum类型课用于switch语句;

BigInteger & BigDecimal

  • BigInteger可以用于表示任意大小的整数,而且它属于不变类,继承自Number,转换为基本类型的方法如下,转换时可通过longValueExact()等方法保证准确;

    类型 方法
    byte byteValue()
    short shortValue()
    int intValue()
    long longValue
    float floatValue
    double doubleValue
  • BigDecimal用于表示一个任意大小且精度完全准确的浮点数;

    import java.math.BigDecimal;
    import java.math.RoundingMode;
    
    public class Main {
        public static void main(String[] args) {
            BigDecimal d1 = new BigDecimal("123.456789");
            
            BigDecimal d2 = d1.setScale(4, RoundingMode.HALF_UP); // 四舍五入,123.4568
            BigDecimal d3 = d1.setScale(4, RoundingMode.DOWN); // 直接截断,123.4567
            
            // 计算小数位数;
            System.out.println(d1.scale);
            System.out.println(d2);
            System.out.println(d3);
        }
    }
    
  • BigDecimal进行比较时,使用equals()方法比较时要求两值相等,且小数位数scale()也相同。所以总是使用compareTo()进行BigDecimal值得比较;

常用工具类

  • Math:提供大量静态方法用于实现数学计算;

    常用方法 功能
    abs 求绝对值
    min 求最小值
    max 求最大值
    sqrt 求二次方根
    exp 求幂
    log 求以e为底的对数
    log10 求以10为底的对数
    random 生成随机数
  • Random:用于创建伪随机数(即只要给定一个初始种子,产生的随机数序列则是完全一致的);

生成随机数类型 方法
int nextInt()
long nextLong()
float nextFloat
double nextDouble
  • SecureRandom:用于创建真随机数(即不可预测的安全的随机数),通过操作系统提供的安全的随机种子来生成随机数;

    import java.util.Arrays;
    import java.security.SecureRandom;
    import java.security.NoSuchAlgorithmException;
    
    public class Main {
        public static void main(String[] args) {
            // 初始化一个对象并初始化为null
            SecureRandom sr = null;
            try {
                sr = SecureRandom.getInstanceStrong(); // 获取高强度安全随机数生成器
            } catch (NoSuchAlgorithmException e) {
                sr = new SecureRandom(); // 获取普通的安全随机数生成器
            }
            byte[] buffer = new byte[16];
            sr.nextBytes(buffer); // 用安全随机数填充buffer
            System.out.println(Arrays.toString(buffer));
        }
    }
    
发布了104 篇原创文章 · 获赞 69 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/github_39655029/article/details/104916234