java基础回顾

一.关键字

关键字:关键字是不能作为变量来定义的,常见关键字:

final object static 八大数据类型[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PAxDHz9j-1576130919810)(C:\Users\Administrator\Desktop\个人收藏\1575943034551.png)]

final:可以修饰 类 变量 方法

类:此类不可以被继承 保证了安全性和一定程度上的设计方便以及性能的提升 String

变量:此变量指向的对象不再可变 日志对象

对于变量加上final 代表这个变量就是一个常量 无法再被更改

比如:

final int x=1; x=2//报错

对于引用加上final代表这个引用再也不能引用其他对象

比如:

final A y=new A();
y.a=1;

这个对象本身的相关属性还是可以更改的。

方法:代表这个方法不可以再被子类重写 独有的方法 代表特定的含义

2. 方法

声明方法不能被子类重写。

private 方法隐式地被指定为 final,如果在子类中定义的方法和基类中的一个 private 方法签名相同,此时子类的方法不是重写基类方法,而是在子类中定义了一个新的方法。

3. 类

声明类不允许被继承。

static

  1. static可以修饰方法 变量

    变量:代表此变量是类中唯一的一份,类级别的变量 不用new实例就可以调用,在静态方法中直接调用,非静态方法中直接可以通过类名.变量取到 通常用来当做全局变量 类加载的时候被赋值

    类所有的实例都共享该变量 静态变量在内存中只存一份

    public class A {
    
        private int x;         // 实例变量
        private static int y;  // 静态变量
    
        public static void main(String[] args) {
            // int x = A.x;  // Non-static field 'x' cannot be referenced from a static context
            A a = new A();
            int x = a.x;
            int y = A.y;
        }
    }
    

    实例变量:实例变量是与实例捆绑的,与实例变量共存亡.

    方法:静态方法是类级别的方法,不用新建一个对象就能够直接调用 可以在非静态方法中调用 也可以在静态方法中调用 对于一些公共的方法适用 省去了new对象的开销

    静态方法在类加载的时候就已经存在,由于不依赖于任何实例,所以静态方法必须有实现,不可以为抽象方法

    public abstract class A {
        public static void func1(){
        }
        // public abstract static void func2();  // Illegal combination of modifiers: 'abstract' and 'static'
    }
    

    静态方法中只能访问类中的静态方法和静态变量 且不可以存在this super之类的关键字 因为这两个关键字都与具体的实例有关

    public class A {
    
        private static int x;
        private int y;
    
        public static void func1(){
            int a = x;
            // int b = y;  // Non-static field 'y' cannot be referenced from a static context
            // int b = this.y;     // 'A.this' cannot be referenced from a static context
        }
    }
    

静态语句块

静态语句块在类加载的时候加载一次

因此可以在静态语句块中进行初始化操作,如数据库连接、初始化图像等。

public class A {
    static {
        System.out.println("123");
    }

    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new A();
    }
}

静态内部类

非静态内部类在创建的时候需要依赖外部类的实例,但是静态内部类则不需要依赖外部类的实例

public class OuterClass {

    class InnerClass {
    }

    static class StaticInnerClass {
    }

    public static void main(String[] args) {
        // InnerClass innerClass = new InnerClass(); // 'OuterClass.this' cannot be referenced from a static context
        OuterClass outerClass = new OuterClass();
        InnerClass innerClass = outerClass.new InnerClass();
        StaticInnerClass staticInnerClass = new StaticInnerClass();
    }
}

静态导包

静态导包 可以在使用静态类的方法时直接用方法即可

import static java.util.stream.Collectors.toList;//静态导包
lamda表达式收集器

初始化顺序

类在初始化的时候 静态变量静态块的优先级是要比实例方法和实例变量高的,会优先加载 静态变量和静态方法还有静态块的顺序,是根据在类中的实际位置来决定的.

public static String staticField = "静态变量";
static {
    System.out.println("静态语句块");
}
public String field = "实例变量";
{
    System.out.println("普通语句块");
}

最后才是构造函数的初始化。

public InitialOrderTest() {
    System.out.println("构造函数");
}

存在继承的情况下,初始化顺序为:

  • 父类(静态变量、静态语句块)
  • 子类(静态变量、静态语句块)
  • 父类(实例变量、普通语句块)
  • 父类(构造函数)
  • 子类(实例变量、普通语句块)
  • 子类(构造函数)
二 .Object

Object在Java中就是先祖级别的角色,所有的对象都要潜在的继承于Object,Object的方法更是有

给这些对象提供了通用性.

public native int hashCode()

public boolean equals(Object obj)

protected native Object clone() throws CloneNotSupportedException

public String toString()

public final native Class<?> getClass()

protected void finalize() throws Throwable {}

public final native void notify()

public final native void notifyAll()

public final native void wait(long timeout) throws InterruptedException

public final void wait(long timeout, int nanos) throws InterruptedException

public final void wait() throws InterruptedException

equals()

1.等价关系

两个对象具有等价性需要满足5个条件:

(1)自反性

x.equals(x); // true

(2)传递性

x.equals(y); y.equals(z); x.equals(z);

(3)一致性

x.equals(y) ..... x.equals(y) //始终是true

(4) 对称性

x.equals(y) == y.equals(x); // true

(5)与null的比较

任何不是null的对象与null对比都是false

x.equals(null); // false;

2.等价与相等

对于基本类型没有equals方法 只有用==来实现等价的效果

对于引用类型的equals方法 只有两个对象的引用地址一致才可以实现等价性和相等性

Integer x = new Integer(1);
Integer y = new Integer(1);
System.out.println(x.equals(y)); // true
System.out.println(x == y);

  1. Object中equals的实现

    (1)比较引用是否一致 如果是一致的直接返回true

    if(Object1==Objetct2) return true;
    
    

    (2) 比较是否是同一个类型 如果不是直接返回false

    if(Object1.getClass()!=Object.getClass() || Object2==null)  return false;
    
    

    (3)将Object进行转型

    T1 t=(T1) Object
    
    

    (4) 比较关键区域的字段是否相等 否则返回false

if(t.x1!=this.x1) return falseif(t.x2!=this.x2) 
    return false;

public class EqualExample {

    private int x;
    private int y;
    private int z;

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        EqualExample that = (EqualExample) o;

        if (x != that.x) return false;
        if (y != that.y) return false;
        return z == that.z;
    }
}

∗ ∗ h a s h C o d e **hashCode hashCode

**

  1. hashCode
    
    

    是根据对象的属性字段加上hash算法生成的随机串值,由于其随机性,导致两个hashCode相等的对象不一样是相等的对象,计算hashCode的方法自己可以指定哪些字段需要参与。

    为什么在覆盖equals方法的同时也要重写hashCode方法?

    因为重写equals方法后,两个对象以新的标准来进行比较,但是两个等价的对象,hashcode必须是相等的,所以为了保证两个对象在重写equals方法过后依然是 保证等价的两个对象 ,hashcode方法必须重写。

    Set集合是保证集合内部没有重复元素,如果对象重写了equals方法.但没有重写hashcode方法,就有可能加入两个等价的对象,破坏了集合的稳定性也打破了当初设计者的初衷.

    hash算法尽量要均匀分布。

    理想的哈希函数应当具有均匀性,即不相等的对象应当均匀分布到所有可能的哈希值上。这就要求了哈希函数要把所有域的值都考虑进来。可以将每个域都当成 R 进制的某一位,然后组成一个 R 进制的整数。

    R 一般取 31,因为它是一个奇素数,如果是偶数的话,当出现乘法溢出,信息就会丢失,因为与 2 相乘相当于向左移一位,最左边的位丢失。并且一个数与 31 相乘可以转换成移位和减法:31*x == (x<<5)-x,编译器会自动进行这个优化。

    @Override
    public int hashCode() {
        int result = 17;
        result = 31 * result + x;
        result = 31 * result + y;
        result = 31 * result + z;
        return result;
    }
    
    

toString方法

1.默认类名+散列码形式

Clone方法

1.由于Clone方法是Object的protect方法 所以需要类重写 其他类的实例才能去调用

实现Clone方法需要:

1.类实现Cloneable接口

2.重写Clone方法

3.抛出classnotsupport异常

package org.ibase4j.model;

public class Example implements Cloneable {

    private int id;

    private String name;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

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

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

    public static void main(String[] args) throws CloneNotSupportedException {
        Example e = new Example(1, "12");
        Example clone = (Example) e.clone();
        System.out.println(clone);
    }
}


浅拷贝:

把源对象的引用地址赋给克隆对象

public class ExampleShallowCopy implements Cloneable {

    private int[] arr;

    public ExampleShallowCopy() {
        arr = new int[10];
        for (int i = 0; i < 10; i++) {
            arr[i] = i;
        }
    }

    public void set(int index, int value) {
        arr[index] = value;
    }

    public int get(int index) {
        return arr[index];
    }

    @Override
    protected ExampleShallowCopy clone() throws CloneNotSupportedException {
        return (ExampleShallowCopy) super.clone();
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        ExampleShallowCopy e1 = new ExampleShallowCopy();
        ExampleShallowCopy e2 = null;
        e2 = e1.clone();
        e1.set(1, 222);
        System.out.println(e2.get(1));
    }
}


深拷贝:

克隆对象和源对象不是引用同一个对象 而是把整个整体全部复制下来

public class ExampleDepieeCopy implements Cloneable {

    private int[] arr;

    public ExampleDepieeCopy() {
        arr = new int[10];
        for (int i = 0; i < 10; i++) {
            arr[i] = i;
        }
    }

    public void set(int index, int value) {
        arr[index] = value;
    }

    public int get(int index) {
        return arr[index];
    }

    @Override
    protected ExampleDepieeCopy clone() throws CloneNotSupportedException {
        ExampleDepieeCopy clone = (ExampleDepieeCopy) super.clone();
        clone.arr=new int[arr.length];
        for (int i = 0; i < clone.arr.length; i++) {
            clone.arr[i]=arr[i];
        }
        return clone;
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        ExampleDepieeCopy e1 = new ExampleDepieeCopy();
        ExampleDepieeCopy e2 = null;
        e2 = e1.clone();
        e1.set(2, 222);
        System.out.println(e2.get(2));
    }
}


clone() 的替代方案

使用 clone() 方法来拷贝一个对象即复杂又有风险,它会抛出异常,并且还需要类型转换。Effective Java 书上讲到,最好不要去使用 clone(),可以使用拷贝构造函数或者拷贝工厂来拷贝一个对象。 手动实现一个属性的复制

public class CloneConstructorExample {

    private int[] arr;

    public CloneConstructorExample() {
        arr = new int[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i;
        }
    }

    public CloneConstructorExample(CloneConstructorExample original) {
        arr = new int[original.arr.length];
        for (int i = 0; i < original.arr.length; i++) {
            arr[i] = original.arr[i];
        }
    }

    public void set(int index, int value) {
        arr[index] = value;
    }

    public int get(int index) {
        return arr[index];
    }
}

CloneConstructorExample e1 = new CloneConstructorExample();
CloneConstructorExample e2 = new CloneConstructorExample(e1);
e1.set(2, 222);
System.out.println(e2.get(2)); // 2

三.继承

1.访问控制符

  • 类可见表示其它类可以用这个类创建实例对象。
  • 成员可见表示其它类可以用这个类的实例对象访问到该成员;

public:访问权限开的最松的控制符,基本所有的范围都可以访问到

private:只被当前类拥有 其他类不可以访问 包括继承 只能内部的set和get方法访问到

protected:只能被自己和子类访问 protected 用于修饰成员,表示在继承体系中成员对于子类可见,但是这个访问修饰符对于类没有意义。

default:可以被同一个包的类的实例访问

里氏替换原则:任何父类出现的地方 子类都可以出现----->所以子类在重写父类的方法时,访问修饰符的级别不可以比父类还要低

抽象类与接口

1.抽象类:抽象类除了是abstract修饰的类,其他根普通的类关系不大,内部既可以有抽象方法也可以有不抽象的方法,子类继承抽象类必须实现父类里面的抽象方法,不然也只能定义为抽象类。

2.接口:接口定义了规范,更加强调功能和行为,不受类本身结构的限制,接口可以多继承,让功能的添加组合更加灵活,接口里面不可以有实现方法,且都默认是未实现的方法,里面定义的变量都默认为常量 static final修饰的常量

抽象类的作用:

[]: https://segmentfault.com/q/1010000013110182/a-1020000013114598

抽象类可以有成员 在子类继承的时候 天然拥有这些成员 这样可以增加代码复用性,这是接口所不能替代的。

3. 比较

  • 从设计层面上看,抽象类提供了一种 IS-A 关系,需要满足里式替换原则,即子类对象必须能够替换掉所有父类对象。而接口更像是一种 LIKE-A 关系,它只是提供一种方法实现契约,并不要求接口和实现接口的类具有 IS-A 关系。
  • 从使用上来看,一个类可以实现多个接口,但是不能继承多个抽象类。
  • 接口的字段只能是 static 和 final 类型的,而抽象类的字段没有这种限制。
  • 接口的成员只能是 public 的,而抽象类的成员可以有多种访问权限。

4. 使用选择

使用接口:

  • 需要让不相关的类都实现一个方法,例如不相关的类都可以实现 Compareable 接口中的 compareTo() 方法;
  • 需要使用多重继承。

使用抽象类:

  • 需要在几个相关的类中共享代码。
  • 需要能控制继承来的成员的访问权限,而不是都为 public。
  • 需要继承非静态和非常量字段。

在很多情况下,接口优先于抽象类。因为接口没有抽象类严格的类层次结构要求,可以灵活地为一个类添加行为。并且从 Java 8 开始,接口也可以有默认的方法实现,使得修改接口的成本也变的很低。

super

super关键字主要用在子类中,可以在子类中进行对父类的调用,super()代表是对父类无参构造方法的调用,super(params)对父类的有参构造函数的调用,super使用在构建子类之前,有些初始化操作需要放在父类中,这时候需要super进行调用,super还可以调用父类的普通方法。子类重写父类的方法,可以调用super进行调用父类的方法。注意:如果父类方法是private 子类无法调用。

public class SuperExample {

    protected int x;
    protected int y;

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

    public void func() {
        System.out.println("SuperExample.func()");
    }
}
public class SuperExtendExample extends SuperExample {

    private int z;

    public SuperExtendExample(int x, int y, int z) {
        super(x, y);
        this.z = z;
    }

    @Override
    public void func() {
        super.func();
        System.out.println("SuperExtendExample.func()");
    }
}
SuperExample e = new SuperExtendExample(1, 2, 3);
e.func();
SuperExample.func()
SuperExtendExample.func()

重写和重载:

重写:子类继承父类可以对父类中的方法进行重写,重写最好用override注解标识,这样可以避免一些规范问题,重写要保证方法名,传入的参数,返回值,顺序多要一样。重写一般用在接口中,主要就是多态性,让方法的调用为了满足里式替换原则,重写有以下三个限制:

  • 子类方法的访问权限必须大于等于父类方法;
  • 子类方法的返回类型必须是父类方法返回类型或为其子类型。
  • 子类方法抛出的异常类型必须是父类抛出异常类型或为其子类型。

使用 @Override 注解,可以让编译器帮忙检查是否满足上面的三个限制条件。更加灵活。这里有一个里氏替换原则是override的作用。

class SuperClass {
    protected List<Integer> func() throws Throwable {
        return new ArrayList<>();
    }
}

class SubClass extends SuperClass {
    @Override
    public ArrayList<Integer> func() throws Exception {
        return new ArrayList<>();
    }
}

在调用一个方法时,先从本类中查找看是否有对应的方法,如果没有再到父类中查看,看是否从父类继承来。否则就要对参数进行转型,转成父类之后看是否有对应的方法。总的来说,方法调用的优先级为:

  • this.func(this)
  • super.func(this)
  • this.func(super)
  • super.func(super)
/*
    A
    |
    B
    |
    C
    |
    D
 */


class A {

    public void show(A obj) {
        System.out.println("A.show(A)");
    }

    public void show(C obj) {
        System.out.println("A.show(C)");
    }
}

class B extends A {

    @Override
    public void show(A obj) {
        System.out.println("B.show(A)");
    }
}

class C extends B {
}

class D extends C {
}
public static void main(String[] args) {

    A a = new A();
    B b = new B();
    C c = new C();
    D d = new D();

    // 在 A 中存在 show(A obj),直接调用
    a.show(a); // A.show(A)
    // 在 A 中不存在 show(B obj),将 B 转型成其父类 A
    a.show(b); // A.show(A)
    // 在 B 中存在从 A 继承来的 show(C obj),直接调用
    b.show(c); // A.show(C)
    // 在 B 中不存在 show(D obj),但是存在从 A 继承来的 show(C obj),将 D 转型成其父类 C
    b.show(d); // A.show(C)

    // 引用的还是 B 对象,所以 ba 和 b 的调用结果一样
    A ba = new B();
    ba.show(c); // A.show(C)
    ba.show(d); // A.show(C)
}

重载:重载是表示方法名一样,但是参数类型,参数个数,参数顺序不一样的方法。千万不能以返回值来区分改方法是否是另一个方法的重载方法。重载其实也就是对应现实世界中同一个事物的不同表达形式。我要去取钱,可以从atm取,也可以去柜台取。但是都是代表取钱这个行为。

反射

反射指的是在任何对象运行时,可以获取它的对应类信息,甚至在该类的编译时期的.class文件不存在也可以获取到。

提到反射,应该可以获取到:

1.Filed 字段 可以用set和get来设置和获取 貌似要设置setAccessable属性为true

2.method 方法 可以用invoke()来调用

3.Constructor:获取该类的构造方法 获取到构造方法可以用newInstance来创建一个对象。

以前遇到过的反射如Spring 的IOC和我们在使用jdbc获取数据库连接的时候,需要使用Class.forName(“com.mysql.jdbc”)来获取数据库驱动.这时候会返回一个Class对象。

反射有三种方法.

1.直接类名.class

2.Class的静态方法 Class.forName

3.调用每个对象的getClass()方法

反射的优点:反射的高自由度让我们使得外部和当前的类产生联系提供了可能,反射让我们的开发更具定制化的特点。

(2)类浏览器和可视化开发环境:其实idea之类的开发工具也是运用了反射的相关机制,让我们提前知道类或者的它的实例其中所拥有的方法。

(3)调试器和测试工具 调试器可以检查一个类里面的私有成员 测试工具可以利用反射来进行调用。

缺点:

(1)反射的性能开销相当大,jvm无法对反射的的编程方式进行优化,反射的操作效率比较低,平时开发尽量不用反射就不用反射。

(2)反射使得程序的安全无法得到保证。

(3)反射让内部暴露,违背了面向对象的抽象性

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值