java点点(一)

2024-05-14 00:32
文章标签 java 点点

本文主要是介绍java点点(一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

进程与线程

        进程指一个应用程序的执行过程,它执有资源(内存)和线程。进程是资源分配的基本单位。每一个进程都拥有一个虚拟的完整的地址空间,并且不同的进程的地址空间是不同的。
        线程是程序中一个单一的顺序控制流程,进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU资源的基本单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。一个进程内的不同线程共享同一块地址空间。
        例如对于一个Android应用来说,一个应用就是一个进程。在应用运行时,不但可以响应用户的点击,同时还可以访问网络。这就是因为响应用户点击和访问网络是运行在两个不同的线程中。系统会快速地在两个线程之间来回的切换,从而不会使用户感觉到卡。
        当一个进程中含有多个线程时,这些线程会操作同一块内存(这些线程所属的进程所执有的内存)。因此,多线程可能会导致内存中的数据的混乱,从而出现一些莫名其妙的数据。

线程的终止

        Thread的run()执行完毕后,线程便自动终止。但有些时候线程会需要线程进行不断地循环执行。在此种情况下,如果想要终止线程可以通过设置旗标的方式进行。
        Thread.stop():立刻中止线程。无论该run()方法中是否还有别的代码未执行。不停止释放线程中的资源。
        interrupt():向线程发送一个中止信号,并不会立即停止线程。调用该方法后,Thread.isInterrupted()返回true。但有例外情况:如果当前线程调用了join()系列或者sleep()系列方法,再调用interrupt()时,join和sleep()会抛出异常(InterruptedException),并且isInterrupted()返回的依旧是false。当线程中调用了Object.wait()系列方法时也是由此。

Object

        在Object类中,含有wait(),notify(),notifyAll()三个方法,它们主要是用于线程同步的。

        在某一线程中调用wait()时,可以使用该线程进入等待状态,直到在别的地方调用了该Object的notify或者notifyAll()。

        notifyAll()和notify()用于唤醒通过该Object对象调用wait()而进行等待状态的线程。只不过前者只是随机唤醒某一个,而后者是唤醒所有的。示例如下:

线程P:

public class ProduceThread extends Thread {@Overridepublic void run() {while (true) {synchronized (Main.lockObj) {while (Main.buf >= 1000) {try {Main.lockObj.wait();} catch (InterruptedException e) {e.printStackTrace();}}Main.buf++;System.out.println(getName() + "--操作了buf:" + Main.buf);Main.lockObj.notifyAll();}}}
}
线程C
public class CustomThread extends Thread {@Overridepublic void run() {while (true) {synchronized (Main.lockObj) {while (Main.buf <= 0) {try {Main.lockObj.wait();} catch (InterruptedException e) {e.printStackTrace();}}Main.buf--;System.out.println(getName() + "--操作了buf:" + Main.buf);Main.lockObj.notifyAll();}}}
}
        对于线程P,C来说,synchronized()时使用的是同一个对象,因此synchronized内容的代码块一次只能有一个线程进行操作,从而保证了一次只会有一个线程对Main.buf进行操作,保证了数据的正确性。

        在Main.lockObj.wait()外面仍旧套了一层while循环。这是为了保证在线程被唤醒时仍旧会进行判断,使线程只有在满足条件的情况下才会执行下面的代码。

        假设将内层while循环换成if。有两个线程C,分别为c1,c2。假设某时刻c1正在执行,且此时buf为1。当c1执行完后,会唤醒p1与c2。并且系统调动的仍旧是c1,只不过c1在进入if块时进入wait状态,系统重新选择了p1。p1执行一次时,唤醒了wait状态的c1。只不过系统一直会执行p1直到p1的if判断成立,使p1进入wait。此时,系统调用c2(注意,c2执行一个循环后p1被唤醒,且c1仍旧处于唤醒状态,且代码停留在wait()处)。当c2将buf的值修改成0后,c2也会进入到wait状态。系统又选择了c1,c1对buf--操作时,buf就变成了-1(c2将buf的值变成了1),并且将c2唤醒。c2再次执行buf--后,buf会成为-2。

        如果使用了while循环就不会出现上述情况,因为每一个线程被唤醒后的第一件事就是进行再次判断,保证了自己执行时是满足条件的。

        finalize():当对象被gc回收时,在回收之前会调用该方法。但如非必要不要重写该方法,一个类被gc时说明这个类已经unreachable,此时应该被回收。如果在这个方法中执行耗时操作,必然导致这个类的回收被延迟。只有在一种情况下可重写该方法该对象中有native对象,必须调用native方法释放该对象

Math

        floor():取离参数最近的且比参数小的整数

        ceil():取离参数最近的且比参数大的整数。如ceil(11.1)为12,ceil(-11.9)为-11        

        round():取离参数最近的整数。它的值可以通过floor(参数+0.5)进行计算。如round(11.5)为12,round(-11.6)为-12,而round(-11.5)为-11。

java内存分配

栈、堆与方法区

        栈:自动分配的连续空间,后进先出。用来放置局部变量。其中栈又分为java栈与native栈。

        堆:不连续,用来放置new出来的对象。

        方法区:是堆中比较特殊的一块,也就是说它也是堆的一部分。用来放置类的信息(类对应的字节码),常量池(字符串常量),static变量。

示例图(截自尚学堂java300集中第31集)如下:


基本数据类型与引用数据类型

        要注意:基本数据类型并不一定都存储在栈中。引用数据类型变量存储的值是一个内存地址,该地址指向了真正存储对象的内存位置。

        要判断基本数据类型在栈中还是在堆中,应看它是局部变量还是全局变量。

        一:在方法中声明的变量,即该变量是局部变量,每当程序调用方法时,系统都会为该方法建立一个方法栈,其所在方法中声明的变量就放在方法栈中,当方法结束系统会释放方法栈,其对应在该方法中声明的变量随着栈的销毁而结束,这就局部变量只能在方法中有效的原因。
        在方法中声明的变量可以是基本类型的变量,也可以是引用类型的变量。(1)当声明是基本类型的变量的时,其变量名及值是放在栈中。(2)当声明的是引用变量时,所声明的变量(该变量实际上是在方法中存储的是内存地址值)是放在方法的栈中,该变量所指向的对象是放在堆类存中的
        二:在类中声明的变量是成员变量,也叫全局变量放在堆中的,同样在类中声明的变量即可是基本类型的变量 也可是引用类型的变量。(1)当声明的是基本类型的变量其变量名及其只时放在堆内存中的。(2)引用类型时,其声明的变量仍然会存储一个内存地址值,该内存地址值指向所引用的对象

        因此,可心总结为:所有的局部变量都放在栈内容中,随着方法的结束而被清出栈。所有的对象都放在堆内存中,而对象中又会封装它自己特有的数据即成员变量,所以只要是成员变量都放在堆中,这跟它是什么类型的数据无关

static

        static变量和方法是存储在方法区中的。由于方法区中存储了类的字节码,所以static方法依旧指向了字节码文件。

        在创建对象时,系统会自动略过static变量和方法static方法和变量从属于类,只要类的信息被加载到方法区后就可以使用它们。在堆中创建对象时,该对象中并没有static的方法和变量。因此,调用static时直接用"类点*",而不是用"对象点*"。

        当使用到一个类时,该类的信息就会被加载到方法区中。此时就可以直接调用其中的static方法和变量,但是由于非static方法和变量并不从属于类,它们只有等到new出对象时才被创建,也就是说:非static的成员和方法是晚于static的成员和方法的,所以static方法中不能调用非static的成员和方法。

        如下图(尚学堂java300集第36集):


this与super

        他们都是隐式参数,每一个非static方法的参数中都含有这两个参数(系统自动传入,不需要我们手动传)。但要注意,static方法中并没有this参数。因为static加载到内存中时,有可能并不存在对象。

        super指的是直接父类对象的引用。一个类的构造方法中,必须有一个构造方法在第一行调用了super(),即使自己没有手动添加,系统也会默认的添加。

        因此,如果父类中没有无参数的构造方法,子类必须有手动调用父类中的某一个构造函数。这也是为什么父类没有无参构造方法时,子类必须要手动添加一个构造方法——为了在该构造方法中调用父类的某个构造方法。当父类中有无参构造方法是,子类可以不用手动添加构造方法——默认时子类的构造方法第一行必然是super(),这是系统为我们自动调用了父类的无参构造方法。

        在构造方法中,this和super都必须出现在第一行,所以它们不能同时出现在同一个构造方法中。如下:

public class ChildClass extends SuperClass {public ChildClass() {this("");System.out.println("child");}public ChildClass(String name){System.out.println("name");}
}
执行new ChildClass()时,输出的结果如下:

继承

        在上面我们知道,子类必须调用父类的构造函数,一直到Object。因此,创建一个对象时,连带着创建了该对象的所有父类的对象,这也是super的直接父类对象的含义。

        在内存中,是采用包裹的方式处理这些对象的。具体如下:


        当调用某一方法是,首先在当前子类中寻找,如果有就执行该方法;如果没有,就从直接父类中寻找,直到找到Object中。如果一直未找到,就意味着当前类中没有该方法。

        从上图可以看出,重写(override)并不意味着直接抹除掉父类中的该方法,而是在调用的时候先找到子类中的该方法,进而执行子类的方法。在子类中该方法执行完毕后就结束寻找,父类中的该方法是不会被执行到的。

        在每一个非static方法中都有this和super两个隐式参数,其中super便是上图中红线的部分。而this指的是最外层的Bird对象,包括里面的Animal对象中的this,Object对象中的this。因此,未重写的方法中调用重写的方法,重写方法是按子类中的代码运行的。如果:A调用了B,子类中重写了B,那么执行A时调用的B便是子类中的。

乱点

        (1),&&的优先级比||高,两者并非同级的。如果(true||true&&false)值为true,因为true&&false值是false,而true||false值为true。如果两者同级的话,那结果应该是false,因为true||true值为true,true&&false值为false。

        (2),接口可以多继承。并且继承的接口中可以有同一个方法,在实现的时候只需要实现一个就行。如a接口中有a1(),b接口中也有a1(),c接口继承了a,b两接口,那么实现c接口的类中只需要重写一个a1()即可。这是因为接口是完全抽象的,同一个方法并不会出现混乱。

        (3),方法中也可以定义类。

        

这篇关于java点点(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/987247

相关文章

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

Spring AI集成DeepSeek的详细步骤

《SpringAI集成DeepSeek的详细步骤》DeepSeek作为一款卓越的国产AI模型,越来越多的公司考虑在自己的应用中集成,对于Java应用来说,我们可以借助SpringAI集成DeepSe... 目录DeepSeek 介绍Spring AI 是什么?1、环境准备2、构建项目2.1、pom依赖2.2