当前位置:首页 » 《关注互联网》 » 正文

【Java】:学习笔记(易错小结)

3 人参与  2024年12月13日 16:02  分类 : 《关注互联网》  评论

点击全文阅读


  ?个人主页:island1314

⛺️  欢迎关注:?点赞 ??留言 ?收藏  ? ? ?


这篇博客意旨大家对于 java 没有注意到的小点,给大家补充一些内容,增加 对 java 的理解

1. 概述

eg1:.class

javac:将 java 源文件(.java 文件)编译为字节码文件 (.class 文件)java :用于运行已经编译好的字节码文件,如:Hello(注意不是 Hello.class)

开发与运行Java程序需要经过的三个主要步骤为

编辑源程序     、编译生成字节码     和       解释运行字节码

eg2:Java 程序运行环境

Java 程序运行环境简称 JVM (Java 虚拟计算机)

补充:Java 的 跨平台特点是由 JVM 保证的,跨平台性 可以让其一次编译,随处运行

eg3: Java 可执行程序

javac.exe:Java 编译器java.exe:Java 运行工具jar.exe:打包工具javac.exe:文档生成工具

eg4: Java 自动回收机制

Java 和 C++ 不同,不需要用 free 关键字来回收,其 通过 垃圾回收器 (Garbage Collector)自动管理内存,程序员不需要手动释放内存当一个内存被当成垃圾从内存中释放的时候,它的  finalize() 方法会被调用

eg4: Java注释

单行注释: // Hello 多行注释: /*s2313 2121321*/文档注释: 以 /** 开头,在注释内容末尾以 */ 结束。其是对一段代码概括性的解释说明,可以 使用 javadoc 命令将文档注释提取出来,生成帮助文档 
/** * @author IsLand1314~* @version 8.0 */
解释:上面的 @author 、@version 是 javadoc 用于文档注释的

2. 语法:

eg1:while 循环体内用的是 boolean

eg2:java 变量定义

java 中 的标识符由数字、字母、下划线、$ 组成,但是不能以 数字 开头,可以以 $  开头,不能是关键字、 true、 false、null 等byte b = 15678,也是不合法的,超过了最大整型

eg3:整型提升

byte a1 = 1;byte a2 = 2;byte a3 = a1 + a2;

这个会报错,因为 byte 被 自动提升为 int 类型,应该写成  byte a3 = (byte) (a1 + a2);

eg4:方法重载

在同一个作用域内,方法名相同但参数个数 或者 参数类型不同的方法

eg5:运算符优先级

设 x = 1 , y = 2 , z = 3,则表达式 y+=z--/++x 的值是

// ++ -- 优先级 > * / %z-- 先返回值在运算,故:返回值是3,z的值变为2++x 先运算再返回值,故:x值变2,返回2;3/2整形数据范围内的计算,返回1;y +=1y = 3;返回y = 3.

eg6:字符常量

在Java的基本数据类型中,char型采用Unicode编码方案

每个 Unicode 码占用 2 字节内存空间,这样,无论是中文字符还是英文字符,都是占用 2 字节内存空间Unicode 字符以  \u 开头,比如 '\u0000' 表示 空白字符

eg7:方法传参问题

? 在 Java 中,所有的对象(包括 String 类型)是通过 引用传递 的,而基本数据类型(如 int、double、boolean 等)是通过 值传递 不过,Java 的 String 类型是不可变的,所以即使你通过引用传递了一个 String 对象,你也不能直接修改这个对象的内容。

为了更好地理解和实现类似“按地址传参”的效果,首先需要澄清一点:

在 Java 中,没有直接的“地址传递”,因为 Java 的所有方法调用都是通过 值传递 的,所谓的“引用传递”其实是传递对象的 引用值,但对不可变类型如 String 无法直接修改原对象

如下:

class test {    public static void main(String[] args) {        int a1  = 3, a2 = 4;        show(a1, a2);        System.out.println("a1 = " + a1  + ", a2 = " + a2);        String s1 = "a", s2 = "b";        show(s1, s2);        System.out.println("s1 = " + s1  + ", s2 = " + s2);        StringBuilder t1 = new StringBuilder("a");        StringBuilder t2 = new StringBuilder("b");        show(t1, t2);        System.out.println("t1 = " + t1  + ", t2 = " + t2); // 内容被修改    }    public static void show(int a, int b) {        a++;        b = a + b;    }    public static void show(String s1, String s2) {        s1 = s1 + "q";        s2 = s2 + s1;    }    public static void show(StringBuilder t1, StringBuilder t2) {        // 修改 StringBuilder 对象        t1.append("q");        t2.append(t1.toString());  // 将 s1 修改后的内容追加到 s2 中    }}

原因:

StringBuilder 是一个可变的字符串类,允许你修改字符串内容(例如使用 .append() 方法)当你传递 StringBuilder 对象到 show 方法时,方法内部的修改会直接影响传入的对象,而不需要返回值。

3. 类和对象

eg1:Java 不支持多继承,一般只有单继承

在Java程序中,通过类的定义只能实现 单重继承,但通过接口的定义可以实现 多重继承关系。

 eg2:instanceof

instanceof 操作符用于检查对象是否是某个类或接口的实例,但 instanceof 不能用于原始类型(如 int)。

instanceof 只能用来检查对象是否是某个类类型的实例,而 1 是一个基本数据类型(int),而不是一个对象。

eg3:在抽象方法中不能有方法体 

抽象方法应该只包含方法签名,而不能有方法体。方法体 {} 是用于定义方法具体实现的,但抽象方法的目的是为了让子类去实现它的具体行为。

eg4:主体类操作

在 Java 中,你不能在类的主体部分直接执行操作,这类操作需要放在方法、构造函数、代码块中

eg5:访问控制级别权限

在 缺省的(default)、private、proctected、public 中哪个的访问控制级别最大

在 Java 中,访问控制级别的大小是根据访问范围来确定的。访问控制的顺序从最严格到最宽松如下:

private:最严格,表示该成员仅能在当前类内部访问,外部无法访问。

default(缺省访问控制,包访问权限):如果没有显式指定访问修饰符(即没有写 private、protected 或 public),则该成员的访问权限是包级别的,意味着它可以被同一包中的其他类访问,但不能被其他包中的类访问。

protected:表示该成员可以被同一包中的类和所有子类(无论子类是否在同一个包中)访问,但不能被其他包中非子类的类访问。

public:最宽松,表示该成员可以被任何类访问,不管它们是否在同一个包中

访问控制级别排序:从最严格到最宽松的顺序为:

private < default < protected < public

因此,public 是访问控制级别最大(最宽松)的,意味着它的访问范围最大,任何类都可以访问

eg6:子类继承父类,默认调用 无参构造

class P{    String s;    public P(){        System.out.println(1);    }    public P(String s){        this.s=s;        System.out.println(2);    }}class S extends P{    P p;    public S(String s){        System.out.println(3);        this.s = s;        p = new P(s);    }    public S(){        System.out.println(3);    }} class test {     public static void main(String[] args) {        S s = new S("is");        }}// 输出: 132
创建一个子类对象时候,子类构造函数会默认调用父类的构造函数(一般都是无参构造函数,隐含的  super 调用)

eg7:有关类、对象和实例的叙述

类是对象的抽象,对象是类的具体化,实例是对象的另一个名称
 

eg8: Object 类是根类

如果定义一个类的时候没有用到 关键字 extends,这个类 也是有 直接父类的。

原因:会将默认继承 Object 类,而不是没有父类。Object 类是 Java 类层次结构的跟类,所有类都直接或者间接继承 自 Object 类

eg9:整数类型和整数类

设有下面两个赋值语句:

a = Integer.parseInt(“12”);
b = Integer.valueOf(“12”).intValue();

下述说法正确的是( D )

A、a是整数类型变量,b是整数类对象
B、a是整数类对象,b是整数类型变量
C、a和b都是整数类对象并且值相等
D、a和b都是整数类型变量并且值相等

eg10:构造方法的特点

方法名与类名相同无返回值自动调用可重载用于对象初始化可以有访问修饰符

eg11:对象比较、字符串比较以及 toString() 方法的使用

class T{    public String s;    public T(String s){        this.s = s;    }}public class Test {    public static void main(String[] args) {        T t1 = new T("123");        T t2 = new T("123");        System.out.println(t1 == t2);        System.out.println(t1.equals(t2));        System.out.println("-----------------");        System.out.println(t1.s == t2.s);        System.out.println(t1.s.equals(t2.s));        System.out.println("-----------------");        System.out.println(t1.toString() == t2.toString());        System.out.println(t1.toString().equals(t2.toString()));    }}// 输出:falsefalse-----------------truetrue-----------------falsefalse

分析如下:

① t1 == t2

== 比较的是两个对象的引用(内存地址)是否相同这里 t1 和 t2 是两个不同的 T 对象(它们分别是通过 new T("123") 创建的)因此它们在内存中是不同的对象,引用不同。所以 t1 == t2 输出 false。

② t1.equals(t2)

equals() 是 Object 类的方法,用于比较两个对象的内容是否相等。默认情况下,Object 类的 equals() 方法与 == 相同,比较的是引用地址但是 T 类并没有重写 equals() 方法,因此调用的是 Object 类的 equals() 方法,这会比较 t1 和 t2 的引用地址。由于 t1 和 t2 是两个不同的对象,它们的引用不同,因此 t1.equals(t2) 也会返回 false

③ t1.s == t2.s

s 是 String 类型字段,String 是一个特殊的类,在 Java 中,它有一个字符串常量池(string pool)当你使用 new String() 创建一个字符串时,它不会直接使用常量池中的字符串,而是创建一个新的 String 对象。但是,String 对象的常量池优化会使得常量字符串直接从池中获取,而不会创建新的对象。在这段代码中,t1.s 和 t2.s 都是 "123",这是一个常量字符串。由于 "123" 是一个常量字面量,它会被自动放入字符串常量池中,因此 t1.s == t2.s 比较的是常量池中的两个相同的字符串引用。所以它们指向相同的内存地址,结果是 true。

④ t1.s.equals(t2.s)

equals() 方法是用来比较字符串内容是否相等的由于 t1.s 和 t2.s 都是 "123",它们的内容是相同的,所以 t1.s.equals(t2.s) 的结果是 true

⑤ t1.toString() == t2.toString()

toString() 方法返回的是对象的字符串表示默认情况下,Object 类的 toString() 方法返回的是对象的类名加上对象的哈希码,类似于 T@15db9742 这样的字符串t1.toString() 和 t2.toString() 会分别返回两个不同对象的 toString() 字符串表示这两个表示不同对象的哈希码,肯定是不同的字符串,因此 t1.toString() == t2.toString() 比较的是两个不同的字符串引用,结果是 false

⑥ t1.toString().equals(t2.toString())

equals() 方法比较的是字符串的内容而非引用由于 t1 和 t2 是两个不同的对象,它们的 toString() 输出内容是不同的(因为不同的对象有不同的哈希码)所以 t1.toString().equals(t2.toString()) 结果是 false。

总结:

== 比较的是对象的引用:用于判断两个对象是否指向同一个内存地址equals() 比较的是对象的内容:如果没有重写 equals() 方法,默认会比较引用地址字符串常量池:对于字符串字面量(如 "123"),它们会从常量池中获取,因此不同的变量可能指向相同的字符串引用toString() 的默认实现:默认返回的是对象的类名和哈希码,且不同对象的 toString() 结果不同

4. Java API

eg1:String 常量池

String s1 = "java";String s2 = "java";String s3 = "ja" + "va";

这个上面创建了几个对象 ?

1String s1 = "java";

"java" 是一个字符串常量。Java 会将字符串常量 "java" 存储在字符串常量池中。

如果 "java" 在常量池中已经存在,那么不会创建新的对象,而是直接使用池中已有的对象。如果它还不存在,JVM 会创建一个新的 "java" 字符串对象并将其存储在常量池中。

对象创建:1 个(存储在常量池中的 "java")

2. String s2 = "java";

"java" 是与 s1 中相同的字符串常量。由于常量池中已经有了 "java" 字符串对象(在步骤 1 中创建的),因此这里不会创建新的对象,s2 直接引用常量池中的 "java" 字符串。

对象创建:0 个(直接引用常量池中的对象)

3. String s3 = "ja" + "va";

这行代码是字符串拼接,使用的是两个常量字符串 "ja" 和 "va"。由于它们都是编译时常量,Java 编译器会在编译时将它们直接拼接成 "java"。

结果是 "java" 会在编译时作为一个常量值替换,等同于写成 String s3 = "java";。

因此,最终 s3 也会引用常量池中的 "java" 字符串对象,而不会创建新的字符串对象。

对象创建:0 个(常量池中已经有 "java",且 s3 引用的是常量池中的对象)

总结:

总共有 1 个对象被创建:"java" 存储在常量池中。

s1、s2 和 s3 都指向同一个常量池中的 "java" 字符串对象。

eg2:Random 类

class test {    public static void main(String[] args) {        Random r1 = new Random();        Random r2 = new Random(13);        Random r3 = new Random(13);        System.out.println(r1.nextInt(100)); // 随机生成一个[0, 100) 的数        System.out.println(r2.nextInt(100)); // 随机生成一个[0, 100) 的数.        System.out.println(r3.nextInt(100)); // 随机生成一个[0, 100) 的数    }}// 运行结果如下:86 92 92

指定相同的随机数种子,则所有对象产生的随机数序列也相同

eg3:简述 String、StringBuffer、StringBuilder 的区别

不可变性和可变性 String:不可变。一旦创建了 String 对象,就不能改变其内容,每次修改都会生成新的 String 对象StringBufferStringBuilder 对象:可变,可以对其进行修改,不产生新的对象线程安全 StringBuffer: 线程安全,大部分方法都是 synchronized 同步的,适合多线程环境StringBuilder:非线程安全,不是同步的,不适合多线程环境,但执行速度更快性能 String:由于不可变性,频繁修改字符串时性能最差。StringBuffer:由于同步机制,性能较 StringBuilder 差,但比 String 好StringBuilder:最快,尤其在执行大量字符串操作时候使用场景 String:字符串不需要修改时使用StringBuffer:多线程环境中对字符串进行操作时使用StringBuilder:单线程环境中或确定不会有多线程访问时使用,提供更好的性能

5. 异常

异常类 都是 继承 java.lang 包下的 Throwable 类

eg1:多个 catch 最多执行一个

如下:

 class test {    public static void main(String[] args) {        int a = 3, b = 0;        char[] c = "abc".toCharArray();        try{            System.out.println(a);            System.out.println(a/b);            System.out.println(c[a]);        }catch (ArithmeticException e){            System.out.println("算数异常");        }catch (ArrayIndexOutOfBoundsException e){            System.out.println("数组下标越界异常");        }catch (Exception e){            System.out.println("系统异常");        }finally {            System.out.println("程序结束");        }    }}// 输出:3算数异常程序结束// 注意:当我们注释掉捕捉 算术异常 的 catch,a/b 就会被系统异常的catch 捕获的

原因:

 try 代码块中,发生异常之后的语句后面代码不会被执行finally 无论是否发生异常都会执行 一个异常处理中可以有 多个 finally 语句块,它们会 按照出现顺序 执行释放资源、关闭文件等操作都是由 finall 完成

eg2:编译时和运行时异常

RuntimeExCeption 类及其子类都是运行时异常,运行时异常都是程序在运行期间,由 Java 虚拟机自动捕获处理, JAVA 编译器 不会对异常 进行检查除了 RuntimeExCeption 类及其子类都是编译时异常,JAVA 编译器 对编译时异常 进行检查

eg3:关键字 throws 和 throw

throws 关键字声明异常,一般写在 方法声明 的后面来抛出异常, throws 还需要声明方法中发生异常的 类型,而且可以抛出多个异常,多个异常之间用 逗号 隔开throw 用于方法体内,抛出的是一个 异常实例,每次只能抛出一个异常实例

eg3:自定义异常

自定义异常必须继承 Excetion 类 或其 子类

eg4:处理编译时异常的两种方式

try ... catch 语句 对异常进行捕获处理

使用 throws 关键字声明抛出异常,让调用者对异常进行处理

6. 泛型

eg1:泛型使用 ? 表示 类型通配符

7. 集合

eg1:简述集合 List、Set、Map 的区别

List:元素有序,可重复。主要实现类有 ArrayList 和 LinkedListSet:元素无序,不可重复。主要实现类有  HashSet 和 TreeSetMap:存储的元素都是以 键(key)、值(Value) 的映射关系,元素都是成对出现的。主要实现类有  HashMap 和 TreeMap

eg2:为什么 ArrayList 的增删操作比较慢,而查找操作比较快

ArrayList 集合的底层是使用一个数组来保存元素,在增加 或 删除指定位置的元素时,会导致创建新的数组,效率较低,因此不适合用来做大量的增删操作但是这种数组结构允许程序通过索引的方式来访问元素,因此使用其进行查找元素很快

8. 线程

eg1:线程启动与运行

创建一个线程之后,不是它启动之后就直接开始运行

需要显示地调用线程的启动方法(如 start() )才能使 线程进入可运行状态


点击全文阅读


本文链接:http://zhangshiyu.com/post/200709.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1