😉😉 学习交流群:
✅✅1:这是孙哥suns给大家的福利!
✨✨2:我们免费分享Netty、Dubbo、k8s、Mybatis、Spring...应用和源码级别的视频资料
🥭🥭3:QQ群:583783824 📚📚 工作微信:BigTreeJava 拉你进微信群,免费领取!
🍎🍎4:本文章内容出自上述:Spring应用课程!💞💞
💞💞5:以上内容,进群免费领取呦~ 💞💞💞💞

文章目录
我们每天都在写方法的调用,但是我们能搞明白其中的原理和JVM当中的操作步骤么?这就是本文的意义。
官方说法:
在JVM中,将符号引用转换为调用方法的直接引用这个操作是跟JVM当中方法的绑定机制息息相关的。
说人话:
上边这段话是什么意思?我这里给大家解释一下,我们javap整理完毕字节码文件之后,我们会可以在任意一个方法中查看code下的字节码指令,很多字节码指令的后边都会跟#数字这么一个概念,这个就是符号引用,这个引用指向常量池。
所谓将符号引用转换为方法的直接引用,就是将这个字节码指令后边的符号引用,转变为真实的方法。
下列中的#3就是符号引用。
- public void methodB();
- descriptor: ()V
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=3, locals=1, args_size=1
- 0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
- 3: ldc #6 // String methodB().....
- 5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
- 8: aload_0
- 9: invokevirtual #7 // Method methodA:()V
- 12: aload_0
- 13: dup
- 14: getfield #2 // Field num:I
- 17: iconst_1
- 18: iadd
- 19: putfield #2 // Field num:I
- 22: return
从上述找一个例子的话,就是将偏移地址为9的字节码指令后边的#7这个符号引用用真实的方法字面量代替
官方说法:
当一个字节码文件被装载进JVM内部时,如果被调用的目标方法在编译期可知且运行期保持不变时。这种情况下将调用方法的符号引用转换为直接引用的过程称之为静态链接。
说人话:
静态链接:这种方式在编译阶段就已经把符号引用直接转换为了直接引用。
官方说法:
如果被调用的方法在编译期无法被确定下来,也就是说,只能够在程序运行期将调用方法的符号引用转换为直接引用,由于这种引用转换过程具备动态性,因此也就被称之为动态链接。
说人话:
动态链接:这种方式在运行阶段才能把符号引用直接转换为直接引用。
绑定是一个字段、方法或者类在符号引用被替换为直接引用的过程,这仅仅发生一次。这个不论是编译器确定还是运行期确定都只会发生一次,不会修改。
对应的方法的绑定机制为:早期绑定 (Early Bindng)和晚期绑定(Late Binding)。
官方说法:
早期绑定就是指被调用的目标方法如果在编译期可知,且运行期保持不变时即可将这个方法与所属的类型进行绑定,这样一来,由于明确了被调用的目标方法究竟是哪一个,因此也就可以使用静态链接的方式将符号引用转换为直接引用。
说人话:
早期绑定是和我们的静态绑定相对应的。
官方说法:
如果被调用的方法在编译期无法被确定下来,只能够在程序运行期根据实际的类型绑定相关的方法,这种绑定方式也就被称之为晚期绑定
说人话:
晚期绑定是和我们的动态绑定相对应的。
- class Animal {
- public void eat(){
- System.out.println("动物进食");
- }
- }
-
- interface Huntable{
- void hunt();
- }
-
- class Dog extends Animal implements Huntable{
- @Override
- public void eat(){
- System.out.println("狗吃骨头");
- }
-
- @Override
- public void hunt() {
- System.out.println("捕食耗子,多管闲事");
- }
- }
-
- class Cat extends Animal implements Huntable{
- @Override
- public void eat(){
- System.out.println("猫吃鱼");
- }
-
- @Override
- public void hunt() {
- System.out.println("捕食耗子,天经地义");
- }
- }
-
- public class AnimalTest{
- public void showAnimal(Animal animal){
- animal.eat();//晚期绑定
- }
-
- public void showHunt(Huntable h){
- h.hunt();//晚期绑定
- }
-
- }


- class Animal {
- public void eat(){
- System.out.println("动物进食");
- }
- }
-
- interface Huntable{
- void hunt();
- }
-
- class Dog extends Animal implements Huntable{
- @Override
- public void eat(){
- super.eat();//早期绑定
- System.out.println("狗吃骨头");
- }
-
- @Override
- public void hunt() {
- System.out.println("捕食耗子,多管闲事");
- }
- }
-
- class Cat extends Animal implements Huntable{
- public Cat(){
- super();//早期绑定
- }
- public Cat(String name){
- this();//早期绑定
- }
-
- @Override
- public void eat(){
- System.out.println("猫吃鱼");
- }
-
- @Override
- public void hunt() {
- System.out.println("捕食耗子,天经地义");
- }
- }
-
- public class AnimalTest{
- public void showAnimal(Animal animal){
- animal.eat();//晚期绑定
- }
-
- public void showHunt(Huntable h){
- h.hunt();//晚期绑定
- }
-
- }
光标放到cat这个类上查看他的jclasslib

invokeSpecial是早期绑定字节码指令,invokevirtual是晚期绑定的字节码指令。
随着高级语言的横空出世,类似于Java一样的基于面向对象的编程语言如今越来越多,尽管这类编程语言在语法风格上存在一定的差别,但是它们彼此之间始终保持着一个共性,那就是都支持封装、继承和多态等面向对象特性
既然这一类的编程语言具备多态特性,那么自然也就具备早期绑定和晚期绑定两种绑定方式。
Java中任何一个普通的方法其实都具备虚函数的特征,也就是运行期才能确定下来,它们相当于c++语言中的虚函数 (c++中则需要使用关键字virtual来显式定义)。
如果在Java程序中不希望某个方法拥有虚函数的特征时,则可以使用关键字final来标记这个方法。也就是一个方法不想被晚期绑定,直接把他给final修饰即可。
文章目录
找到学习一门新技术的,没有任何视频的时候,如何找到这个突破口呢?
1:是个啥?
2:能干嘛?
3:去哪下在?
4:怎么玩?
5:常用的玩法套路是什么?
6:永远的helloworld
没有docker之前:
假定您在开发任何一个企业应用,您使用的是一台笔记本电脑而且您的开发环境具有特定的配置。
其他开发人项身处的环境配置也各有不同。您正在开发的应用依赖于您当前的配置且还要依赖于某些配置文件。
此外,您的企业还拥有标准化的测试和生产环境,且具有自身的配置和一系列支持文件。您希望尽可能多在本地模拟这些环境而不产生重新创建服务器环境的开销。
请问?您要如何确保应用能够在这些环境中运行和通过质量检测? 并且在部署过程中不出现令人头疼的版本、配置问题,也无需重新编写代码和进行放障修复?
答案就是使用容器。Docker之所以发展如此迅迷,也是因为它对此给出了一个标准化的解决方案----系统平滑移植,容器虚拟化技术。
环境配置相当麻烦,换一台机器,就要重来一次,费力费时。很多人想到,能不能从根本上解决问题,软件可以带环境安装? 也就是说,安装的时候,把原始环境一模一样地复制过来。开发人员利用 Docker 可以消除协作编码时“在我的机器上可正常工作”的问题。
Docker的核心点就是:软件可以带环境进行安装和部署。
有Docker之后:
首先我们的源代码是百分之百跑通过的,其次我们的源码+配置+环境+版本可以整体打包迁移到另外一个环境当中,也就是实现了所谓的软件携带环境进行安装和部署
说的再详细一些,就是将我们的源码、配置、环境、版本将这些内容打成一个镜像文件也就是所谓的包。我们大家一定用过VMware,里边安装过CentOs,里边的CentOs是假的,但是和我们真实的CentOs是一模一样的。
这是为什么呢?我们在VMware里边安装的时候,选择了ISO这个镜像文件。这个CentOS可以打成一个镜像,那么咱们的源码、配置、环境、版本也可以打包成一个镜像文件。这个镜像跑在Docker引擎中,这个时候模拟出来的这个环境将会让我们的环境和配置与开发时一模一样。

之前在服务器配置一个应用的运行环境,要安装各种软件,Java/RabbtMQ/MYSQLIJDBC驱动包等。安装和配置这些东西有多麻烦就不说了,它还不能跨平台。
假如我们是在 Windows 上安装的这些环境,到了 Linux 又得重新装。况且就算不跨操作系统,换另一台同样操作系统的服务器,要移植应用也是非常麻烦的。
传统上认为,软件编码开发/测试结束后,所产出的成果即是程序或是能够编译执行的二进制字节码等(ava为例)。而为了让这些程序可以顺利执行开发团队也得准备完整的部署文件,让维运团队得以部署应用程式,开发需要清楚的告诉运维部署团队,用的全部配置文件+所有软件环境。不过,即便如此,仍然常常发生部署失败的状况。
Docker的出现使得Docker得以打破过去【程序即应用】观念。透过镜像(images)将作业系统核心除外,运作应用程式所需要的系统环境,由下而上打包,达到应用程式跨平台间的无缝接轨运作。
打个比方来讲:之前我们搬家是搬房子里边的东西,现在搬家是直接把整栋楼甚至是整个小区直接搬过去,里边的任何东西都没变!
Docker开发的理念就是:一次镜像,处处运行。
从windows系统迁移到linux系统的时候。比方说我们在任何品牌的window电脑安装Linux的时候都会要求安装一个VMware软件。然后在VMware安装完毕之后,可以在VM上安装特定的Linux的ISO即可。这样的话,虽然大家电脑品牌和底层window都不一样,但是我们最终运行了一个Linux的ISO文件,这样就保证了环境迁移的一致性。
Docker基于这种思想,屏蔽了操作系统的差异化。Redis、MySQL、MQ等等和各种各样的配置文件+源码+配置文件+特殊引导第三方组件打成一个包或者叫镜像文件,跑在Docker的执行引擎中。
这里边Docker代替了VMware的作用。里边的镜像文件就类似于之前的Linux的ISO镜像文件。
Docker是基于Go语言实现的云开源项目。主要目标是:一次镜像,处处运行。
也就是通过对应用组件的封装、分发、部署、运行等生命周期的管理,使用户的APP(可以是一个WEB应用或数据库应用等等)及其运行环境能够做到真正做到:一次镜像,处处运行
Linux 容器技术的出现就解决了这样一个问题,而 Docker 就是在它的基础上发展过来的。将应用打成镜像,通过镜像成为运行在Docker容器上面的实例,而 Docker究器在任何操作系统上都是一致的,这就实现了跨平台、跨服务器。只需要一次配置好环境,换到别的机子上就可以一键部署好,大大简化了操作。
