Java笔记

Java篇

学习开源项目JavaGuide笔记

JVM 、 JDK 、 JRE

JVM 是Java虚拟机,运行Java字节码。针对不同系统有特定的实现,给出相同的结果。字节码和不同系统的JVM是实现一次编译,随处运行的关键。
JDK是Java Development Kit,包含JRE,编译器javac,工具(javadoc和jdb),能够创建和编译程序。
JRE是Java运行时环境,是运行已编译Java程序所需内容的集合,包含JVM,Java类库,Java命令和其他的一些基础构件,但是不能用于创建新程序。
区分呢?如果只是运行一下程序,那就JRE就行,编程的话需要JDK。
在这里插入图片描述

字节码

JVM可以理解的代码叫做字节码(.class),不面向特定的处理器,只面向虚拟机。
.java文件通过javac编译 =》.class文件通过解释器&JIT(just-in-time compilation) 编译器转换成机器人可理解的代码。当 JIT 编译器完成第一次编译后,其会将字节码对应的机器码保存下来,下次可以直接使用。
在这里插入图片描述

为什么说 Java 语言“编译与解释并存”?

Oracle JDK vs OpenJDK

Java和C++

  • 都是面向对象的语言,支持封装、继承、多态
  • Java不提供指针来直接访问内存,程序内存更加安全
  • Java的类单继承 ,C++可以多继承,Java的接口可以多继承
  • Java有自动内存管理垃圾回收机制GC,不需要手动释放内存
  • C++同时支持方法重载和操作符重载,Java只支持方法重载

基本语法

字符型常量和字符串常量的区别

字符常量是单引号引起的一个字符,字符串常量是双引号引起的 0 个或若干个字符。字符常量相当于一个整型值( ASCII 值),可以参加表达式运算; 字符串常量代表一个地址值(该字符串在内存中存放位置)。
字符常量只占2个字节,注意char在Java中是占两个字节。

静态方法不能调用非静态成员

静态方法是属于类的,在类加载的时候就会分配内存,可以通过类名直接访问。非静态成员属于实例对象,只有在对象实例化之后才存在,需要通过类的实例对象去访问。
在调用方式上的区别:调用静态方法可以通过类名.方法,也可以对象.方法,实例方法就只能对象.方法,相当于调用静态方法不需要创建对象,但是不建议对象.方法进行调用,容易误解。

重载和重写

重载:发生在同一类中,方法名必须相同,参数类型不同,个数不同,顺序不同,方法返回值和访问修饰符可以不同。也就是多个同名方法根据不同传参来执行不同的逻辑处理。
重写:发生在运行期,是子类对父类允许访问的方法的实现过程进行重新编写。子类返回值类型比父类返回值类型更小或相等,抛出异常范围小于等于父类,修饰符范围大于等于父类。如果方法的返回类型是 void 和基本数据类型,则返回值重写时不可修改。但是如果方法的返回值是引用类型,重写时是可以返回该引用类型的子类的。

==和equal()

对于基本类型,==比较的是值

对于引用数据类型来说,==比较的是对象的内存地址.
引用类型变量存的值是对象的地址

在Object类中的equals()方法

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

String 中的equals方法是重写过的,因为Object中的这个方法比较的是对象的内存地址,但是String的equals方法比较的是对象的值。
创建String类型的对象时候,虚拟机会在常量池找有没有存在的值和要创建的值相同的对象,如果有的话直接赋给当前引用,没有就在常量池重新创建一个String对象。下面是String中的equals方法

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}
hashCode() 与 equals()

为什么重写equals()的时候必须重写hashCode()
hashCode()的作用是获取哈希码(int 整数),也称散列码。这个哈希码的作用是确定该对象在哈希表中的索引位置。
hashCode()定义在Object类中,所以Java的所有类都包含hashCode()函数,该方法通常用来将对象的内存地址转换为整数之后返回。
其实hashCode()和equals()都是用于比较两个对象是否相等,hashCode()可以大大缩小查找的成本。之所以不只提供hashCode呢,是因为两个对象的hashCode相等,不代表两个对象相等(哈希碰撞)。
如果两个对象的hashCode相等,并且equals()返回true,才认为这两个对象相等。如果hashCode不相等,则可以直接认为这两个对象不相等。

8种基本数据类型
  • 6种数字类型
    • 4种整数型 byte short int long(Java使用long的数据后面加上L,不然将作为整形解析
    • 2种浮点型 float double
  • 1种字符类型 char
  • 1种布尔型 boolean
    这八种基本类型都有对应的包装类分别为:Byte、Short、Integer、Long、Float、Double、Character、Boolean 。 包装类型不赋值就是 Null ,而基本类型有默认值且不是 Null

面向对象基础

面向对象和面向过程的区别:

解决问题的方式不同:

  • 面向过程把解决问题的过程拆成一个个方法,通过一个个方法的执行解决问题
  • 面向过程会先抽象出对象,然后通过对象执行方法的方式解决问题。
成员变量和局部变量的区别:
  • 语法形式:成员变量属于类,局部变量是在代码块或方法中定义的变量或者是方法的参数。局部变量不能被访问修饰符和static修饰,但是都能被final修饰。
  • 存储方式:如果成员变量是使用static修饰的,那这个成员变量就是属于类的,否则这个成员变量就是属于实例的。对象存在于堆内存,局部变量存在于栈内存。
  • 生存时间:成员变量是对象的一部分,随着对象的创建而存在,局部变量随着方法的调用而自动消失。
  • 默认值:成员变量如果没有被赋初值,会自动以类型的默认值复制(final修饰的必须显式赋值),局部变量不会自动赋值。
创建一个对象用什么运算符?对象实体与对象引用有何不同?

new运算符,new创建对象实例(对象实例在堆内存中),对象引用指向对象实例(对象引用存放在栈内存)。一个对象引用可以指向0或1个对象(一根绳子可以不绑气球也可以绑一个气球),一个对象可以有 n个引用指向它(可以n个绳子绑一个气球)

对象的相等和指向他们的引用相等有什么不同?
  • 对象的相等一般比较的是内存中存放的内容是否相等
  • 引用相等一般比较的是他们指向的内存地址是否相等
一个类的构造方法的作用是什么,如果一个类没有声明构造方法程序能正确执行吗?

构造方法的主要作用是完成对该类对象的初始化工作
可以执行,因为一个类有默认不带参数的构造方法,如果我们添加了自己的构造方法(无论是否有参),Java都不会再添加默认的构造方法。

构造方法有什么特点?能不能被重载?
  • 名字和类名相同
  • 没有返回值,但是不能加void
  • 生成类的对象时候自动执行,不需要调用

构造方法不能重写,但是可以重载,可以有多个构造函数。

Java常用对象

Object

Object是特殊的类,也是所有类的父类。提供以下11钟方法:

public final native Class<?> getClass()//native方法,用于返回当前运行时对象的Class对象,使用了final关键字修饰,故不允许子类重写。

public native int hashCode() //native方法,用于返回对象的哈希码,主要使用在哈希表中,比如JDK中的HashMap。
public boolean equals(Object obj)//用于比较2个对象的内存地址是否相等,String类对该方法进行了重写用户比较字符串的值是否相等。

protected native Object clone() throws CloneNotSupportedException//naitive方法,用于创建并返回当前对象的一份拷贝。一般情况下,对于任何对象 x,表达式 x.clone() != x 为true,x.clone().getClass() == x.getClass() 为true。Object本身没有实现Cloneable接口,所以不重写clone方法并且进行调用的话会发生CloneNotSupportedException异常。

public String toString()//返回类的名字@实例的哈希码的16进制的字符串。建议Object所有的子类都重写这个方法。

public final native void notify()//native方法,并且不能重写。唤醒一个在此对象监视器上等待的线程(监视器相当于就是锁的概念)。如果有多个线程在等待只会任意唤醒一个。

public final native void notifyAll()//native方法,并且不能重写。跟notify一样,唯一的区别就是会唤醒在此对象监视器上等待的所有线程,而不是一个线程。

public final native void wait(long timeout) throws InterruptedException//native方法,并且不能重写。暂停线程的执行。注意:sleep方法没有释放锁,而wait方法释放了锁 。timeout是等待时间。

public final void wait(long timeout, int nanos) throws InterruptedException//多了nanos参数,这个参数表示额外时间(以毫微秒为单位,范围是 0-999999)。 所以超时的时间还需要加上nanos毫秒。

public final void wait() throws InterruptedException//跟之前的2个wait方法一样,只不过该方法一直等待,没有超时时间这个概念

protected void finalize() throws Throwable { }//实例被垃圾回收器回收的时候触发的操作
String
String、StringBuffer、StringBuilder 的区别?String 为什么是不可变的?

final修饰的类不能被继承,修饰的方法不能被重写,修饰的变量是基本数据类型则值不能改变,修饰的变量是引用类型则不能再指向其他对象

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
    private final char value[];
	//...
}

String不可变的原因

  • 保存字符串的数组被final修饰并且是私有,String类没有提供和暴露修改这个字符串的方法
  • String类被final修饰,导致不能被继承,避免了子类破坏String不可变

StringBuffer和StringBuilder都继承AbstractStringBuilder ,AbstractStringBuilder 也是使用字符数组来保存字符串,但是没有使用final和private关键字进行修饰,这个类还提供了字符串修改方法append等。

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    char[] value;
    public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }
  	//...
}
线程安全性
  • String中的对象是不可变的,也就是常量,线程安全。
  • StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以线程安全
  • StringBuilder没有加同步锁,所以是非线程安全的。
性能
  • 每次对String类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String对象
  • StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用。
  • 相同情况下StringBuilder获得的性能提升只有10%-15%,但是有线程不安全的风险
总结
  • 操作少量数据:String
  • 单线程操作字符串缓冲区下操作大量数据:StringBuilder
  • 多线程操作字符串缓冲区下操作大量数据:StringBuffer
字符串拼接+和StringBuilder

对象引用和"+“的字符串拼接方式,实际上是通过StringBuffer调用append()实现的,拼接完成之后调用toString()得到一个String对象。
在循环中使用”+"拼接字符串的缺陷:编译器不会创建单个的StringBuilder复用,而是创建多个StringBuilder对象,如果直接使用StringBuilder对象进行字符串拼接的话,就不会存在这个问题了。

String#equals() 和 Object#equals() 有何区别?

String中的equals方法是被重写过的,比较的是字符串的值是否相等,Object的是比较对象的内存地址。

字符串常量池的作用

JVM为了提高性能和减少内存小号针对字符串(String类)专门开辟的一块区域,主要是避免字符串的重复创建

String aa = "ab"; // 放在常量池中
String bb = "ab"; // 从常量池中查找
System.out.println(aa==bb);// true

JDK1.7之前运行时常量池逻辑包含字符串常量池存放在方法区,JDK1.7以后拿到了堆中。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值