Java基础--乱炖

链接: Java基础–乱炖.

一、静态方法、实例方法

1. 静态方法、实例方法的区别?

  • 静态方法比较少用,因为他在一启动就实例化了,比较占资源,静态方法在程序开始时生成内存,实例方法在程序运行中生成内存,静态方法配合单例模式还是比较好用的,比较多的用在数据连接上,避免使用的原则就是减少资源消耗,还有会在工具类里面用。
  • 静态就是类的,实例就是对象的。 静态方法不需要依赖类当中的属性,能在这个方法中封闭的完成一个功能。实例方法更多的会使用到类当中的属性;简单点说,静态方法用来执行无状态的一个完整操作,实例方法则相反,它通常是一个完整逻辑的一部分,并且需要维护一定的状态值。
  • 在外部调⽤静态⽅法时,可以使⽤"类名.方法名"的⽅式,也可以使⽤"对象名.方法名"的⽅式。
  • 静态⽅法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),⽽不允许
    访问实例成员变量和实例⽅法
    实例方法则⽆此限制

2. 静态方法为什么只允许访问静态成员?

**结论:**静态方法只能访问静态成员,实例方法可以访问静态和实例成员。之所以不允许静态方法访问实例成员变量,是因为 实例成员变量是属于某个对象的,而静态方法在执行时,并不一定存在对象。

Java程序启动class文件被读取时类被加载,如果有static方法,此时会分配内存,非static方法实例化类时才在内存中分配控件存储,引用存储在堆栈中,实体存储在堆中

类加载过程: 加载 => 连接 => 初始化。连接过程⼜可分为三步: 验证 => 准备 => 解析
类加载过程
详情参考文章标题二《二、类的生命周期》

代码示例:

class  A {
    // 类变量
    static int a;
    String name;
    int id;
    // 静态代码块
    static {
        a = 10;
        System.out.println("这是父类的静态代码块" + a);
    }
    // 构造代码块
    {
        id = 11;
        System.out.println("这是父类的构造代码块id:" + id);
    }
    A() {
        System. out .println( "这是父类的无参构造函数");
    }
    A(String name) {
        System. out .println( "这是父类的name" + name);
    }
}

class B extends A {
    String name;
    static int b;
    static {
        b = 12;
        System.out.println("这是子类的静态代码块" + b);
    }
    B(String name) {
        super();
        this.name = name;
        System.out.println("这是子类的name:" + name);
    }
}

/**
 * @Description
 * @Author QNut
 * @Date
 * @Version 1.0
 **/
public class Test {
    public static void main(String[] args) {
        B bb= new  B( "GG" );
    }
}

运行结果
静态代码在类的初始化阶段被初始化。

非静态代码则在类的使用阶段(也就是实例化一个类的时候)才会被初始化。

Ⅰ. 静态变量
  • 静态变量是基本数据类型,这种情况下在类的外部不必创建该类的实例就可以直接使用
  • 静态变量是一个引用。这种情况比较特殊,主要问题是由于静态变量是一个对象的引用,那么必须初始化这个对象之后才能将引用指向它。

因此如果要把一个引用定义成static的,就必须在定义的时候就对其对象进行初始化。

Ⅱ. 静态方法

与类变量不同,方法(静态方法与实例方法)在内存中只有一份,无论该类有多少个实例,都共用一个方法。

Ⅲ. 静态代码块

在java类中,可以将某一块代码声明为静态的。

静态代码块主要用于类的初始化。它只执行一次,并且在同属于一个类的main函数之前执行。

静态代码块的特点主要有:

  • 静态代码块会在类被加载时自动执行。
  • 静态代码块只能定义在类里面,不能定义在方法里面。
  • 静态代码块里的变量都是局部变量,只在块内有效。
  • 一个类中可以定义多个静态代码块,按顺序执行。
  • 静态代码块只能访问类的静态成员,而不允许访问实例成员。
Ⅳ. 静态函数

需要在项目启动的时候就初始化,在不创建对象的情况下,其他程序来调用的时候,需要使用静态方法,这种代码是被动执行的。

二、类的生命周期

类的生命周期

1. 加载

我们编写一个java类的代码,经过编译之后生成一个后缀名为.class的文件,java虚拟机就能识别这种文件。java的生命周期就是class文件从加载到消亡的过程。 关于加载,其实,就是将源文件的class文件找到类的信息将其加载到方法区中,然后在堆区中实例化一个java.lang.Class对象,作为方法区中这个类的信息的入口。但是这一功能是在JVM之外实现的,主要的原因是方便让应用程序自己决定如何获取这个类在不同的虚拟机实现的方式不一定相同,hotspot虚拟机是采用需要时在加载的方式,也有其他是先预先加载的。
(可以参考《深入理解JVM》这本书)

2. 连接

连接一般是加载阶段和初始化阶段交叉进行,过程由以下三部分组成:

(1).验证: 确定该类是否符合java语言的规范,有没有属性和行为的重复,继承是否合理,总之,就是保证jvm能够执行
(2).准备: 主要做的就是为由static修饰的成员变量分配内存,并设置默认的初始值

默认初始值如下:

  1. 基本数据类型默认的初始值是0
  2. 引用类型默认的初始值是null
  3. 有static final修饰的会直接赋值,例如:static final int x=10;则默认就是10.

(3).解析: 这一阶段的任务就是把常量池中的符号引用转换为直接引用,说白了就是jvm会将所有的类或接口名、字段名、方法名转换为具体的内存地址。

3. 初始化

初始化这个阶段就是将静态变量(类变量)赋值的过程,即只有static修饰的才能被初始化,执行的顺序就是:父类静态域或子静态代码块,然后是子类静态域或者子类静态代码块 (静态代码块先被加载,然后再是静态属性)

4. 使用

在类的使用过程中依然存在以下三步:

(1)对象实例化:就是执行类中构造函数的内容,如果该类存在父类JVM会通过显示或者隐示的方式先执行父类的构造函数,在堆内存中为父类的实例变量开辟空间,并赋予默认的初始值,然后在根据构造函数的代码内容将真正的值赋予实例变量本身,然后,引用变量获取对象的首地址,通过操作对象来调用实例变量和方法
(2)垃圾收集:当对象不再被引用的时候,就会被虚拟机标上特别的垃圾记号,在堆中等待GC回收
(3)对象的终结:对象被GC回收后,对象就不再存在,对象的生命也就走到了尽头
5.类卸载
类卸载即类的生命周期走到了最后一步,程序中不再有该类的引用,该类也就会被JVM执行垃圾回收,从此生命结束…

三、Java序列化

更详细的java序列化知识:java序列化,看这篇就够了.

1. 序列化的含义、意义及使用场景

  • 序列化:将对象写入到IO流中
  • 反序列化:从IO流中恢复对象
  • 意义:序列化机制允许将实现序列化的Java对象转换位字节序列,这些字节序列可以保存在磁盘上,或通过网络传输,以达到以后恢复成原来的对象。序列化机制使得对象可以脱离程序的运行而独立存在。
  • 使用场景:所有可在网络上传输的对象都必须是可序列化的,比如RMI(remote method invoke,即远程方法调用),传入的参数或返回的对象都是可序列化的,否则会出错;所有需要保存到磁盘的java对象都必须是可序列化的。通常建议:程序创建的每个JavaBean类都实现Serializeable接口。

2. 序列化总结

  1. 所有需要网络传输的对象都需要实现序列化接口,通过建议所有的javaBean都实现Serializable接口。
  2. 对象的类名、实例变量(包括基本类型,数组,对其他对象的引用)都会被序列化;方法、类变量、transient实例变量都不会被序列化。
  3. 如果想让某个变量不被序列化,使用transient修饰。
  4. 序列化对象的引用类型成员变量,也必须是可序列化的,否则,会报错。
  5. 反序列化时必须有序列化对象的class文件。
  6. 当通过文件、网络来读取序列化后的对象时,必须按照实际写入的顺序读取。
  7. 单例类序列化,需要重写readResolve()方法;否则会破坏单例原则。
  8. 同一对象序列化多次,只有第一次序列化为二进制流,以后都只是保存序列化编号,不会重复序列化。
  9. 建议所有可序列化的类加上serialVersionUID 版本号,方便项目升级。

四、Java中的IO流

1. Java 中 IO 流分为⼏种

  • 按照流的流向分,可以分为输⼊流和输出流;
  • 按照操作单元划分,可以划分为字节流和字符流;
  • 按照流的⻆⾊划分为节点流和处理流。

Java Io 流共涉及 40 多个类,这些类看上去很杂乱,但实际上很有规则,⽽且彼此之间存在⾮常紧密
的联系, Java I0 流的 40 多个类都是从如下 4 个抽象类基类中派⽣出来的

  • InputStream/Reader: 所有的输⼊流的基类,前者是字节输⼊流,后者是字符输⼊流。
  • OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。

按操作⽅式分类结构图:
按操作⽅式分类结构图
按操作对象分类结构图:

按操作对象分类结构图

2. 既然有了字节流,为什么还要有字符流?

问题本质想问: 不管是⽂件读写还是⽹络发送接收,信息的最⼩存储单元都是字节,那为什么 I/O 流操作要分为字节流操作和字符流操作呢?

回答: 字符流是由 Java 虚拟机将字节转换得到的,问题就出在这个过程还算是⾮常耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。所以, I/O 流就⼲脆提供了⼀个直接操作字符的接⼝,⽅便我们平时对字符进⾏流操作。如果⾳频⽂件、图⽚等媒体⽂件⽤字节流⽐᫾好,如果涉及到字符的话使⽤字符流⽐较好。

3. BIO,NIO,AIO 有什么区别?

  • BIO (Blocking I/O): 同步阻塞 I/O 模式数据的读取写⼊必须阻塞在⼀个线程内等待其完成。在活动连接数不是特别⾼(⼩于单机 1000)的情况下,这种模型是⽐᫾不错的,可以让每⼀个连接专注于⾃⼰的 I/O 并且编程模型简单,也不⽤过多考虑系统的过载、限流等问题。线程池本身就是⼀个天然的漏⽃,可以缓冲⼀些系统处理不了的连接或请求。但是,当⾯对⼗万甚⾄百万级连接的时候,传统的 BIO 模型是⽆能为⼒的。因此,我们需要⼀种更⾼效的 I/O 处理模型来应对更⾼的并发量。
  • NIO (Non-blocking/New I/O): NIO 是⼀种同步⾮阻塞的 I/O 模型,在 Java 1.4 中引⼊了NIO 框架,对应 java.nio 包,提供了 Channel , Selector, Buffer 等抽象。 NIO 中的 N 可以理解为 Non-blocking,不单纯是 New。它⽀持⾯向缓冲的,基于通道的 I/O 操作⽅法。 NIO提供了与传统 BIO 模型中的 Socket 和 ServerSocket 相对应的 SocketChannel 和ServerSocketChannel 两种不同的套接字通道实现,两种通道都⽀持阻塞和⾮阻塞两种模式。阻塞模式使⽤就像传统中的⽀持⼀样,⽐较简单,但是性能和可靠性都不好;⾮阻塞模式正好与之相反。对于低负载、低并发的应⽤程序,可以使⽤同步阻塞 I/O 来提升开发速率和更好的维护性;对于⾼负载、⾼并发的(⽹络)应⽤,应使⽤ NIO 的⾮阻塞模式来开发
  • AIO (Asynchronous I/O): AIO 也就是 NIO 2。在 Java 7 中引⼊了 NIO 的改进版 NIO 2,它是异步⾮阻塞的 IO 模型。异步 IO 是基于事件和回调机制实现的,也就是应⽤操作之后会直接返回,不会堵塞在⾥,当后台处理完成,操作系统会通知相应的线程进⾏后续的操作。 AIO 是异步 IO 的缩写,虽然 NIO 在⽹络操作中,提供了⾮阻塞的⽅法,但是 NIO 的 IO ⾏为还是同步的。对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程⾃⾏进⾏ IO 操作, IO 操作本身是同步的。查阅⽹上相关资料,我发现就⽬前来说 AIO 的应⽤还不是很⼴泛, Netty 之前也尝试使⽤过 AIO,不过⼜放弃了。

五、深拷贝、浅拷贝

  1. 浅拷贝: 对基本数据类型进⾏值传递,对引⽤数据类型进⾏引⽤传递般的拷贝,此为浅拷贝。
  2. 深拷贝: 对基本数据类型进⾏值传递,对引⽤数据类型,创建⼀个新的对象,并复制其内容,此
    为深拷贝。
    深拷贝vs浅拷贝

参考资料:

Java基础知识面试题.

java中类加载与静态变量、静态方法与静态代码块详解与初始化顺序.

https://www.cnblogs.com/lubocsu/p/5099558.html.

https://www.cnblogs.com/ipetergo/p/6441310.html.

java序列化,看这篇就够了.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值