2020.3.15

一.java中getClass()方法简介

public class Test {
    public static void main(String[] args) {
        Person p = new Person(1,"刘德华");
        System.out.println(p.getClass());  
        System.out.println(p.getClass().getName()); 
    }
}

class Person{
    int id;
    String name;
    public Person(int id, String name) {
        super();
        this.id = id;
        this.name = name;
    }
}

在这里插入图片描述

1. getClass()返回Class类型的对象。

返回的是内存中实实在在存在的Person 这个类
在这里插入图片描述

2. 返回类型为Class理解:

一个男人有爸爸的称呼,丈夫(老公),还有儿子的称呼,还有爷爷,叔叔等等的称呼,而这些称呼的参照物不同导致有不同的称呼。当参照物是老婆时,这个男人的称呼是丈夫(老公),当参照物是男人的儿子时,此男人的称呼是爸爸。以此类推等等。

同理对于Person类,或者准确的说每个类(除所有类的父类Object)也有两个称呼,既可以称为是类,也可称为变量。参照物不同称呼不同。

上面的代码为例:
参照物: Person的属性id,name 则:Person的称呼是类(Class)
参照物: Class类 则:Person的称呼是变量/属性

例如:
String name
Class Person

name的类型是String,此时 Class Person 的性质和String name一样。
String 等同于 Class,是类。
name 等同于 Person,是对象 / 变量的称呼。

String类下有很多方法供name对象使用,同理Class类下也有很多方法供Person对象使用。

Class类和String类一样,都是继承于Object类,Object是所有类的根类 。

3. getClass()方法的作用

获得了Person这个(类)Class,进而通过返回的Class对象获取Person的相关信息,比如:获取Person的构造方法,方法,属性有哪些等等信息。

3.1 获取Person的相关信息

当我们想要获取Person的属性id或者想要调用Person的方法时,我们需要new Person()来创建一个Person的对象,通过对象来获取。

Person p = new Person(1,”刘德华”); 
p.id // 对象调用属性来获取p的id属性

同理,当我们想要获取Person的信息,比如:获取Person的名字,获取Person的构造函数,获取Person的继承关系等等这些Person的相关信息就不能仅仅只是通过new Person()的方式了
而是需要获取内存中实实在在存在的这个Class Person,通过获取到的。

Person p = new Person(1,”刘德华”); 
Class cla = p.getClass(); // 通过对象p调用getClass()方法返回Class

然后通过cla对象来获取Person的相关信息,因为Class提供了大量的方法来获取类(?extends Object)的信息。

String perName = cla.getName(); // 返回Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称

记住: java是面向对象的。创建对象,通过对象来调用方法。

总结:
获取Person的相关信息步骤(这只是其中一种方法来获取,还有其他方法以后相继补上):**

1.创建对象: Person p = new Person(1,”刘德华”);
2.返回Class类型的对象: Class c = p.getClass();
此时c是Class类型,Class提供了一系列的方法来获取类的相关信息,可以通过对象c来获取Person的信息。比如,获取Person这个类的类名:
String perName = c.getName();

二.Object之hashCode方法

1、java.lang

类 Object

java.lang.Object
public class Object
类 Object 是类层次结构的根类。每个类都使用 Object 作为超类。
所有对象(包括数组)都实现这个类的方法。注意:每个类都直接或者间接继承自这个Object类。

Object类的方法:
(1)、public int hashCode(): 返回该对象的哈希码值
注意:哈希码值是根据哈希算法计算出来的一个值,这个值和对象的实际内存地址有关,但不是对象的实际地址。你可以理解为是对象的内存地址。

(2)、public final Class getClass():返回此Object 的运行时类,返回的 Class 对象是由所表示类的 static synchronized 方法锁定的对象。
java.lang
类 Class
Class 类的方法:public String getName(): 以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。

三.java中的不可变类与String的不可变机制

不可变类

不可变类的意思是创建该类的实例后,该实例的实例变量是不可改变的。

Java提供的8个包装类和java.lang.String类都是不可变类,当创建它们的实例后,其实例的实例变量不可改变

创建自定义的不可变类,需满足以下规则:
使用private和final修饰符来修饰该类的成员变量,保证类不会被继承

仅为类的成员变量提供getter方法,不提供setter方法.为了避免通过getter对类的内部可变成员对象进行直接操作导致成员变量发生改变,我们要在getter方法中,返回克隆对象而不是对象本身,并返回对象的拷贝。

如果有必要,重写Object类的equals()方法和hashCode()方法。equals()方法根据关键成员变量来作为两个对象是否相 等的标准,除此之外,还应该保证两个用equals()方法判断为相等的对象的hashCode()也相等。

提供带参数的构造器,用于根据传入参数初始化类里的成员变量,注意进行深拷贝。

public final class MyImmutableDemo {
private final int[] myArray;
public MyImmutableDemo(int[] array) {
this.myArray = array.clone(); //错误写法:this.myArray=array;
}
}

设计一个不可变类,尤其要注意其引用类型的成员变量。如果引用类型的成员变量的类是可变的,就必须采取必要的措施来保护该成员变量所引用的对象不会被修改,这样才能创建真正的不可变类。

String对象的不可变性

string对象在内存创建后就不可改变,不可变对象的创建一般满足以上5个原则,我们看看String代码是如何实现的。

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence
{
    /** The value is used for character storage. */
    private final char value[];
    /** The offset is the first index of the storage that is used. */
    private final int offset;
    /** The count is the number of characters in the String. */
    private final int count;
    /** Cache the hash code for the string */
    private int hash; // Default to 0
    ....
    public String(char value[]) {
         this.value = Arrays.copyOf(value, value.length); // deep copy操作
     }
    ...
     public char[] toCharArray() {
     // Cannot use Arrays.copyOf because of class initialization order issues
        char result[] = new char[value.length];
        System.arraycopy(value, 0, result, 0, value.length);
        return result;
    }
    ...
}

1.String类被final修饰,不可继承
2.string内部所有成员都设置为私有变量
3.不存在value的setter
4.并将value和offset设置为final。
5.当传入可变数组value[]时,进行copy而不是直接将value[]复制给内部变量.
6.获取value时不是直接返回对象引用,而是返回对象的copy.

String对象的不可变性的优缺点

从上一节分析,String数据不可变类,那设置这样的特性有什么好处呢?我总结为以下几点:

字符串常量池的需要.
字符串常量池可以将一些字符常量放在常量池中重复使用,避免每次都重新创建相同的对象、节省存储空间。但如果字符串是可变的,此时相同内容的String还指向常量池的同一个内存空间,当某个变量改变了该内存的值时,其他遍历的值也会发生改变。所以不符合常量池设计的初衷。
2. 线程安全考虑。
同一个字符串实例可以被多个线程共享。这样便不用因为线程安全问题而使用同步。字符串自己便是线程安全的。

类加载器要用到字符串,不可变性提供了安全性,以便正确的类被加载。譬如你想加载java.sql.Connection类,而这个值被改成了myhacked.Connection,那么会对你的数据库造成不可知的破坏。

支持hash映射和缓存。
因为字符串是不可变的,所以在它创建的时候hashcode就被缓存了,不需要重新计算。这就使得字符串很适合作为Map中的键,字符串的处理速度要快过其它的键对象。这就是HashMap中的键往往都使用字符串。

需要注意的是,String对象并不是真的不可变,我们还是可以通过反射机制来修改不可变的对象的。代码例子如下:

 //创建字符串"Hello World", 并赋给引用s
    String s = "Hello World"; 
    System.out.println("s = " + s); //Hello World

    //获取String类中的value字段
    Field valueFieldOfString = String.class.getDeclaredField("value");
    //改变value属性的访问权限
    valueFieldOfString.setAccessible(true);

    //获取s对象上的value属性的值
    char[] value = (char[]) valueFieldOfString.get(s);
    //改变value所引用的数组中的第5个字符
    value[5] = '_';
    System.out.println("s = " + s);  //Hello_World

输出:s = Hello World
s = Hello_World

四.java中的“==”和equals的区别

java中object源码中的的equals方法

public boolean equals(Object obj) {
        return (this == obj);
    }

boolean equals()

  • 源码中用==来比较两个对象是否相等
  • ==基本数据类型比较的是值,引用数据类型比较的是地址值
  • equals()只可以用在引用类型来的比较,比较的是地址值,基本类型无法比较。
  • ==和equals()
  • ==是比较运算符,可以比较基本数据类型,也可以比较引用数据类型
  • equals()方法只能比较的引用数据类型,比较的是地址值,它的底层就是用==做比较,只传入的是引用数据类型(可以看上面我复制的java中equals的底层代码)

五.final关键字的基本用法

1、修饰类

当使用 final 修饰一个类时,表示这个类不能被继承。所以在自己设计一个类时,如果不想继承则可以将类设置为 final ,一般在设计工具类时我们往往都会设计成一个 final 类,例如 JDK 中的 String 、System等。
要点:

被 final 修饰的类,类中的成员变量可以根据自己的实际需要设计为 final。
通常 final 类中的成员方法都会被隐式的指定为 final 方法。

2、修饰方法

被 final 修饰的方法不能被重写

要点:

一个类的 private 方法会隐式的被指定为 final 方法。
如果父类中有 final 修饰的方法,那么子类中不能去重写。
final修饰的方法表示此方法已经是“最后的、最终的”含义,亦即此方法不能被重写(可以重载多个final修饰的方法)。
此处需要注意的一点是:
因为重写的前提是子类可以从父类中继承此方法,如果父类中final修饰的方法同时访问控制权限为private,将会导致子类中不能直接继承到此方法,因此,此时可以在子类中定义相同的方法名和参数,此时不再产生重写与final的矛盾,而是在子类中重新定义了新的方法。

3、修饰成员变量

必须要赋值初始值,而且只能初始化一次
被 final 修饰的成员变量赋值有两种方式:
1、直接赋值
2、全部在构造方法中赋值。
如果修饰的成员变量是基本类型,则表示这个变量的值不能改变。
如果修饰的成员变量是一个引用类型,则是说这个引用的地址的值不可以改变,但是这个引用所指向的对象里面的内容还是可以改变的。
当函数的参数类型声明为final时,说明该参数是只读型的。即你可以读取使用该参数,但是无法改变该参数的值。

六.java中的接口

1. 概述

接口是一种公共规范标准,例如日常生活中的电源插座接口,无论是电风扇也好,还是电视机也好,不同的电器都可以用同一个接口。这里的接口就是多个电器种类的公共规范。

在Java中,接口就是多个类的公共规范,接口是一种引用数据类型,最重要的内容就是其中的抽象方法。

1.1 如何定义一个接口的格式?

public interface 接口名称{
    //接口内容
}

2. 接口的抽象方法的定义

在任何版本的java中,接口都能定义抽象方法

格式:public abstract 返回值类型 方法名称(参数列表); 是没有大括号的哦
注意事项:

1.接口当中的抽象方法,修饰符讲究,权限修饰符只能是public或者干脆不写,还有一个修饰符必须是abstract或者干脆不写
2.也就是说这两个关键字修饰符,可以选择性忽略。(初学者,不推荐

//定义一个接口叫 MyIntefaceAbstract
public interface MyIntefaceAbstract {
    //这是一个抽象方法,推荐写法!
    public abstract void methodAbs1();
    
    //这也是抽象方法,省略关键字 public
    abstract void methodAbs2();
    
    //这也是抽象方法,省略关键字 abstract
    public void methodAbs3();
    
    //这也是抽象方法,省略关键字 public和 abstract
    void methodAbs4();
}
————————————————
版权声明:本文为CSDN博主「阿福97」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43836046/article/details/97160405

3. 接口的抽象方法使用

3.1 使用步骤

3.1.1 接口不能直接使用,必须有一个“实现类”来“实现”该接口,实现类其实和子类差不多,只不过换了个说法(实现就是抽象到具体的过程、手段)

3.1.2 格式:

public clsss 实现类名称 implements 抽口名称{
    //....
}

3.1.3 接口的实现类必须覆盖重写(实现)接口中所有的抽象方法。

实现怎么写?去掉抽象方法中的abstract关键字,加上方法体大括号就行。

3.1.4 创建实现类的对象,进行使用

3.2 代码演示:实现类如何写

3.2.1 先创建一个MyInterfaceAbstract接口

public interface MyIntefaceAbstract {
    //这是一个抽象方法
    public abstract void methodAbs1();

    //这也是抽象方法,省略关键字public
    abstract void methodAbs2();

    //这也是抽象方法,省略关键字abstract
    public void methodAbs3();

    //这也是抽象方法,省略关键字public和abstract
    void methodAbs4();
}


3.2.2 再来弄一个MyInterfaceAbstract接口的实现类MyInterfaceImpl`

public class MyInterfaceImpl implements MyIntefaceAbstract{
    //在IDEA编译器中,鼠标放在implements上,然后 Alt + 回车,选择自动生成下列
    //重写抽象方法methodAbs1
    @Override
    public void methodAbs1() {
        System.out.println("这是第一个方法");
    }

    //重写抽象方法methodAbs2
    @Override
    public void methodAbs2() {
        System.out.println("这是第二个方法");
    }

    //重写抽象方法methodAbs3
    @Override
    public void methodAbs3() {
        System.out.println("这是第三个方法");
    }

    //重写抽象方法methodAbs4
    @Override
    public void methodAbs4() {
        System.out.println("这是第四个方法");
    }
}

3.2.3 最后在main中新建一个实现类MyInterfaceImpl的对象,并使用它的方法

public class Demo03Main {
    public static void main(String[] args) {
        MyInterfaceImpl myInterface = new MyInterfaceImpl();
        myInterface.methodAbs1();  //这是第一个方法
        myInterface.methodAbs2();  //这是第二个方法
    }
}


3.3 注意:

如果实现类并没有覆盖重写接口中的所有的抽象方法,那么这个实现类自己就必须是抽象类。这就好比它把那个抽象方法继承下来了,并没有重写它,抽象方法所在的类必须是抽象类。

4. 接口中常量的定义和使用

接口当中也可以定义“成员变量”,但是必须使用public static final三个关键字进行修饰

从效果上看,这其实就是接口的【常量】。

格式:

public static final 数据类型 常量名称 = 数据值
1
备注:final关键字修饰,代表它就是常量

注意事项:

接口当中的常量,可以省略public static final,但是,编译器也会默认帮忙加上

接口当中的常量,必须进行赋值,必须在声明时就把它给同时赋值了

接口中常量的名称应该要全部用大写字母表示,如果有多个单词应该用下划线分开,这是好习惯,例如下面的班级人数

public static final NUM_OF_CLASS = 36;

5. 继承父类并实现多个接口

使用接口的时候,需要注意:

9.1 接口是没有静态代码块或者构造方法的

9.2 一个类的直接父类是唯一的,但是一个类可以同时实现多个接口,下面是格式

public class 实现类 implements 接口1,接口2…{
//覆盖重写所有的抽象方法
}
1
2
3
9.3 如果实现类所实现的多个接口当中,存在重复的抽象方法,那么只需要覆盖重写一次即可。比如接口A和接口B都有method()方法,那么实现类只需要重写一次method()方法。

9.4 如果实现类没有覆盖重写所有的抽象方法,也不想再写了,那这个实现类就要在class前面加上abstract关键字,声明该实现类是一个抽象类

9.5 接口中的默认方法我们在实现类中可以对它重写也可以不重写,但是如果实现类所实现的多个接口中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行重写

6. 接口之间的多继承

6.1 类与类之间是单继承的。直接父类只有一个

6.2 类与接口之间是多实现的,一个类可以实现多个接口

6.3 接口与接口之间是多继承的,并且要注意两点:①多个父接口中的抽象方法如果重复,没关系。②多个父接口当中的默认方法如果重复,那么子接口必须进行默认方法的覆盖重写,而且带着default关键字。代码示例:
创立一个MyInterface_A接

public interface MyInterface_A{
    //MyInterface_B 接口也有这个抽象方法名,重复
    public abstract void methodCommon(); 

    //MyInterface_B 接口也有这个默认方法名,重复
    public default void methodDefault(){
        System.out.println("AAA");
    }
}

创立一个MyInterface_B接口

public interface MyInterface_B {
    //MyInterface_A 接口也有这个方法名,重复
    public abstract void methodCommon();

    //MyInterface_A 接口也有这个默认方法名,重复
    public default void methodDefault(){
        System.out.println("BBB");
    }
}

创立一个MyInterface接口,同时继承MyInterface_A接口和MyInterface_B接口

public interface MyInterface extends MyInterface_A, MyInterface_B{
    //继承了 MyInter_A接口和 MyInter_B接口重复的 methodCommon方法,并且什么事都没有,重复就重复
    
    public abstract void method(); //MyInterface接口独有的抽象方法

    //继承的抽象方法名重复不碍事,默认方法名一旦重复,在子接口中必须重写
    @Override
    default void methodDefault() {
        System.out.println("重写了接口A和接口B中重复的默认方法");
    }
}

创立一个MyInterface接口的实现类MyInterfaceImpl

public class MyInterfaceImpl implements MyInterface {
    
    //A 接口和 B 接口都有一个抽象方法叫 methodCommon(),实现类中只需重写一次
    @Override
    public void methodCommon() {

    }

    //重写 MyInterface 接口本身具有的抽象方法 method
    @Override
    public void method() {

    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值