代码审计学习-1(JAVASE遇见的问题和简单思考)
2024-4-14 13:2:40 Author: www.freebuf.com(查看原文) 阅读量:0 收藏

前言

学习代码审计之前,需要去知道Javase是什么。我自己简单归纳总结下:就是Java语言的标准版,通用版本。与其对应的是JavaEE,企业版。就像有些软件有社区版和专业版,大体类似如此。

因此,前置条件是去学习Java的语法,一些关键词用法,在此不一一例举了,此文章只记录学习遇见的问题及自己的思考。

1.Java为什么能跨平台?

都知道Java是一门跨平台语言,但是为什么跨平台呢?我知道是因为Java虚拟机的存在,但是Java虚拟机的底层究竟是怎么工作的?为此去查找了一些资料,下面说一下我的个人理解,不一定准确,欢迎大家指正。

1.1java虚拟机

Java虚拟机(Java Virtual Machine,简称JVM)是Java平台的核心组成部分之一,它是一个能够在计算机上运行Java字节码的虚拟机。JVM作为Java应用程序和底层操作系统之间的中间层,提供了跨平台的特性,使得Java程序可以在不同的操作系统和硬件上运行。

这是官方解释,说一下我个人的理解,就是我们输入的代码,代码放在哪?.class文件里,这个文件就是字节码,Java虚拟机运行这个文件,通过规则(就像apple=苹果)解释,把他编译(翻译)成机器能懂的语言。

1.2虚拟机在哪?

说一下3种概念。这块直接官方概念。

1. **JDK(Java Development Kit)**:
- JDK是用于开发Java应用程序的工具包。
- 它包含了开发工具和JRE。
- **开发工具**:提供了编译器(`javac`)、打包工具(`jar`)、文档生成工具(`Javadoc`)等,用于开发Java程序。
- **JRE**:JDK中包含了JRE,因此可以用于运行Java程序。

2. **JRE(Java Runtime Environment)**:
- JRE是一个安装包,用于运行Java程序,而不是开发Java程序。
- 它包含了Java虚拟机(JVM)和核心类库,以及其他支持文件。
- JRE适用于那些只想运行Java程序的最终用户。

3. **JVM(Java Virtual Machine)**:
- JVM是Java虚拟机,是Java程序的执行引擎。
- 它包含在JDK和JRE中。
- JVM负责解释和执行Java程序编译后生成的字节码文件(`.class`文件)。
- JVM将Java程序逐行执行,因此也被称为解释器。

总之,JDK用于开发和执行Java程序,JRE仅用于运行Java程序,而JVM是Java程序的核心执行环境。

因此,为什么不同操作系统对应不同Java环境,就像英语apple翻译成日文和中文需要的翻译包也不一样啊。但是你只要使用apple,有对应的翻译包,就知道你是什么意思。这是跨平台的本质。最后对应的实物苹果就是最底层的机器语言。

2.强制类型转换是怎么进行的?类型不对还会转换吗?

强制类型转换是低精度转高精度是自动的,但是高转低需要强制,因为可能会造成精度丢失之类的。但是如果类型不对呢?

通过查阅一些文章,我发现,在转换类型的时候,会先进行一个判断,判断你转换之后的类型是否符合,不符合会报错。什么意思呢?

简单举列子!

Object obj = new String("Hello");
String str = (String) obj; // 强制类型转换

上面是可以成功的,在这个例子中,obj是Object类型的引用,但它实际上指向一个String对象。强制类型转换(String) obj告诉JVM将obj的引用作为String类型处理。JVM会检查obj引用的实际对象是否真的是String类型,如果是,则转换成功;如果不是,则会在运行时抛出ClassCastException。

你要是转换成整型之类的肯定报错,因为int里面没有“hello”啊!

3.类对象的创建与引用是怎么进行的

我之前一直以为,当我们写完一个类,就会在内存中申请一个空间,然后以这个内存为模板进行接下来的操作,包括各种引用。其实不是,因为假如这个类没有被引用怎么办呢?不就造成资源浪费?

了解一些东西之后,才发现,这就涉及Java的内存回收机制。当一个类被第一次引用的时候(确实需要使用了),才会给其分配资源,当这个类不在被引用的时候,就回收资源。

如何确定不再引用?这个就是有一个专门的程序叫垃圾收集器(Garbage Collector, GC)负责确定对象是否不再被引用,并决定是否可以回收这些对象。Java使用的主要方法是可达性分析算法(Reachability Analysis)。这个就不多说了,主要想知道这个类对象的创建和引用机制。

还有一种比较特殊的就是匿名对象,常常用来调用其内部的每个方法,随用随丢。存活的周期比较短。

4.关于private的看法纠正

之前我觉得,假如一个类定义了一个private变量,那么这个变量也可以被这个类的变量实体引用。后来学习过程中才发现,只可以被这个类本身所用,该类的实例也不行。例子如下

public class Animal{
    private int legs;          //将属性legs定义为private,只能被Animal类内部访问
    public void setLegs(int i){  //在这里定义方法 eat() 和 move() 
        if (i != 0 && i != 2 && i != 4){
             System.out.println("Wrong number of legs!");
             return;
        }
        legs=i;
    }
    public int getLegs(){
        return legs;
    }
}
public class Zoo{
    public static void main(String args[]){
        Animal xb= new Animal();
        xb.setLegs(4);      //xb.setLegs(-1000);       
         xb.legs=-1000;      //非法
        System.out.println(xb.getLegs());
    }
}

这个的目的是为了隐藏类的具体实现细节。

但是private方法和变量是不同的。

修饰符同一个类同一个包子类整体
privateYesNONONO
defaultYesYesNONO
protectedYesYesYesNO
publicYesYesYesYes

5.static关键字作用是什么?

上一个问题说到private,概念弄清发现跟我记忆里的static冲突了,因此记录一下。

1. **静态成员变量**:
- 使用 `static` 关键字修饰的成员变量(也称为类变量)属于整个类,而不是类的实例。
- 这意味着所有类的实例共享相同的静态变量。
- 静态变量在类加载时初始化,并且只初始化一次。

2. **静态方法**:
- 使用 `static` 关键字修饰的方法是类级别的方法,而不是实例级别的方法。
- 静态方法可以通过类名直接调用,而不需要创建类的实例。
- 静态方法不能访问实例变量,因为它们没有隐式的 `this` 引用。

3. **静态代码块**:
- 静态代码块是在类加载时执行的代码块。
- 它用于初始化静态变量或执行其他静态操作。
- 静态代码块只执行一次,通常用于初始化静态资源。

4. **静态内部类**:
- 使用 `static` 关键字修饰的内部类是静态内部类。
- 静态内部类不依赖于外部类的实例,可以直接通过外部类名访问。

总之,`static` 关键字用于定义类级别的成员,而不是实例级别的成员。

6.不同流处理的差别在哪?

Java主要的流处理是通过InputStream 和 Reader 进行输入流处理,OutputStream 和 Writer输出流处理。但是差别在哪?为啥要区分。

这个其实是因为文件的主要组成基本单位不一样,按处理的单位,分为字节流(8 位的字节)、字符流(16 位的字节)。

很好理解,比如中文和一个字和英文字母的一个字组成就不一样。举个例子说明。

FileInputStream和FileOutputStream是用于处理文件的字节流,而Reader和Writer接口及其实现类则是用于处理字符流的。这两者在Java的IO体系中各自扮演着不同的角色,它们的主要区别在于处理的数据类型以及适用的场景。

数据类型:

FileInputStream和FileOutputStream:它们处理的是字节数据,即二进制数据。字节流是通用的,可以用于处理任何类型的数据文件,包括文本文件、图像文件、音频文件等。
Reader和Writer接口及其实现类:它们处理的是字符数据。字符流主要用于处理文本数据,如读取和写入文本文件中的字符。字符流在内部处理时会根据默认的字符集或指定的字符集将字符转换为字节序列,因此在处理文本文件时更为方便。

编码问题:

使用字节流处理文本文件时,需要手动处理编码问题,因为字节流不会进行字符到字节的自动转换。这可能导致在处理不同编码的文本文件时出现乱码问题。
字符流则会自动处理字符编码问题,它可以根据默认的字符集或指定的字符集将字符转换为字节序列,因此在处理文本文件时通常更为安全和方便。

性能:

在处理大量文本数据时,字符流可能会比字节流慢一些,因为字符流在内部需要进行字符到字节的转换。但在处理文本文件时,由于字符流的便利性,这种性能损失通常是值得的。
在处理非文本文件(如图像、音频等)时,字节流则更为高效,因为它直接处理字节数据,无需进行额外的转换。

总结来说,FileInputStream和FileOutputStream以及Reader和Writer的主要区别在于它们处理的数据类型以及适用的场景。在选择使用字节流还是字符流时,需要根据具体的应用场景和需求来决定。如果处理的是文本文件且需要自动处理字符编码问题,那么字符流是更好的选择;如果处理的是二进制文件或需要更高的性能,那么字节流则更为合适。

但是如果采用不匹配的流处理会发生什么呢?能进行处理吗?

实际上还是可以的,但是会出现问题,如果在使用Java的IO流时,数据类型与所选择的流类型不对应,可能会产生以下后果:

乱码问题:

当使用字节流(如FileInputStream或FileOutputStream)读取或写入文本文件时,如果没有正确处理字符编码,很可能会出现乱码。这是因为字节流不会根据字符编码自动转换字符和字节,它只是简单地读取或写入字节序列。如果文件的字符编码与程序默认的字符编码不一致,就会导致乱码。

数据损坏:

如果使用字符流(如Reader或Writer)来处理非文本文件(如图像、音频或二进制数据文件),可能会导致数据损坏。字符流在读取或写入时会根据字符编码进行转换,这可能会改变原始数据的字节表示,从而破坏数据的完整性。

性能问题:

虽然数据类型不匹配不一定会导致性能问题,但在某些情况下,使用不合适的流类型可能会降低性能。例如,对于大量文本数据的处理,使用字节流可能需要进行额外的字符编码和解码操作,这会增加处理时间。相反,如果处理的是非文本文件而使用了字符流,则可能因为不必要的编码转换而降低效率。

异常和错误:

在某些情况下,数据类型不匹配可能会导致运行时异常或错误。例如,当尝试使用字符流读取一个不是文本文件的文件时,可能会抛出IOException或其子类异常,指示读取操作失败。

为了避免这些问题,应该根据数据的实际类型选择合适的IO流。对于文本文件,应该使用字符流(如FileReader、FileWriter、BufferedReader、BufferedWriter等),并指定正确的字符编码(如果需要)。对于非文本文件(如图像、音频或二进制数据文件),应该使用字节流(如FileInputStream、FileOutputStream、BufferedInputStream、BufferedOutputStream等)。这样可以确保数据的正确读取和写入,并避免上述问题的发生。


文章来源: https://www.freebuf.com/articles/others-articles/397879.html
如有侵权请联系:admin#unsafe.sh