java基础知识总结(一)

java基础相关,提纲参考javaguide
阅读提示:参考紧随标题

java语言特点和环境

javaguide:https://snailclimb.gitee.io/javaguide/#/docs/java/basis/java%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86%E6%80%BB%E7%B
B%93

1. 语言特点

https://zhuanlan.zhihu.com/p/32221103

1.简单性(主要是与c++对比)

把C++语言中一些复杂特征去掉,eg Java不支持go to语句,代之以提供break和continue语句以及异常处理。Java还剔除了C++的操作符过载(overload)和多继承特征,并且不使用主文件,免去了预处理程序。因为Java没有结构,数组和串都是对象,所以不需要指针。Java能够自动处理对象的引用和间接引用,实现自动的无用单元收集gc,使用户不必为存储管理问题烦恼。

2. 面向对象

面向对象使用类和对象实现

  • 类:描述多个实体的共同特征,是一种抽象模板。是数据和操作数据的方法的集合。数据和方法一起描述对象(object)的状态和行为。类是按一定体系和层次安排的,使得子类可以从超类继承行为。在这个类层次体系中有一个根类,它是具有一般行为的类。Java程序是用类来组织的。
  • 对象:描述实体个体,是类的实例类(class)。每一对象是其状态和行为的封装。
1. 面向对象编程的优点
  • 可重用性:代码重复使用,减少代码量,提高开发效率。下面介绍的面向对象的三大核心特性(继承、封装和多态)都围绕这个核心。
  • 可扩展性:指新的功能可以很容易地加入到系统中来,便于软件的修改。
  • 可管理性:能够将功能与数据结合,方便管理。
2. 面向对象三个特征

https://www.cnblogs.com/chenssy/p/3351835.html

继承、封装、多态

1. 继承

继承是使用已存在的类的定义作为基础建立新类,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。快速地创建新的类,可以提高代码的重用,程序的可维护性,节省大量创建新类的时间 ,提高我们的开发效率。java只单继承

  • 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有。
  • 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
  • 子类可以用自己的方式实现父类的方法。
2. 封装

把一个对象的状态信息(也就是属性)隐藏在对象内部,不允许外部对象直接访问对象的内部信息。但是可以提供一些可以被外界访问的方法来操作属性

https://www.cnblogs.com/x54256/p/8410142.html

封装体现:属性的封装、方法的封装、类的封装、组件的封装、模块化封装、系统级封装…

封装优点

  • 保护类中的信息,阻止在外部定义的代码随意访问内部代码和数据。
  • 隐藏细节信息,一些不需要程序员修改和使用的信息不展示。
  • 有助于建立各个系统之间的松耦合关系,提高系统的独立性。内部实现方式变化但接口不变就能正常用。
  • 提高软件的复用率,降低成本。每个系统都是一个相对独立的整体,可以在不同的环境中得到使用。
3. 多态

多态的含义:指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定。
不修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变。相同消息不同类做出不同响应。
对于面向对象而言,多态分为编译时多态和运行时多态。其中编辑时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的函数,通过编辑之后会变成两个不同的函数,在运行时谈不上多态。而运行时多态是动态的,它是通过动态绑定来实现的,也就是我们所说的多态性

向上转型:父类类型的引用可以调用父类中定义的所有属性和方法,子类特有的属性和方法不能访问。子类的重载(函数名同参数不同)父类的方法也不能被调用(属于子类特有,向上转型为父类引用时丢失),只能调用父类的被重载的方法;子类覆盖的父类的方法调用的一定是子类的这个方法。

多态性体现在两方面:方法的重载和覆盖、对象的多态性

  • 对象类型和引用类型之间具有继承(类)/实现(接口)的关系;
  • 引用类型变量发出的方法调用的到底是哪个类中的方法,必须在程序运行期间才能确定;
  • 多态不能调用“只在子类存在但在父类不存在”的方法;
  • 如果子类重写了父类的方法,真正执行的是子类覆盖的方法,如果子类没有覆盖父类的方法,执行的是父类的方法。

对象多态:父类可以有不同的子类,向上转型:将子类实例转为父类引用自动,向下转型:将父类实例转为子类实例强制转换,类型不对报异常

java实现多态有三个必要条件:继承、覆盖、向上转型。
继承:在多态中必须存在有继承关系的子类和父类。
覆盖:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
向上转型:在多态中子类对象的引用是父类,只有这样该引用才能够具备技能调用父类的方法和子类的方法

多态实现方法

  • 基于继承实现的多态
    继承是通过覆盖父类的同一方法的几个不同子类来体现的
    对于引用子类的父类类型,在处理该引用时,它适用于继承该父类的所有子类,子类对象的不同,对方法的实现也就不同,执行相同动作产生的行为也就不同。当子类覆盖父类的方法被调用时,只有对象继承链中的最末端的方法才会被调用。
  • 基于接口实现的多态
    指向接口的引用必须是指定这实现了该接口的一个类的实例,在运行时,根据引用的实际类型来执行对应的方法。

instanceof关键字
我们可以通过instanceof关键字来判断某个对象是否属于某种数据类型。如学生的对象属于学生类,学生的对象也属于人类。

3. 可靠性

Java是一个强类型语言,它允许扩展编译时检查潜在类型不匹配问题的功能。Java要求显式的方法声明,它不支持C风格的隐式声明。可靠性方面最重要的增强之一是Java的存储模型。Java不支持指针,它消除重写存储和讹误数据的可能性。类似地,Java自动的“无用单元收集”预防存储漏泄和其它有关动态存储分配和解除分配的有害错误。Java解释程序也执行许多运行时的检查,诸如验证所有数组和串访问是否在界限之内。异常处理是Java中使得程序更稳健的另一个特征。异常是某种类似于错误的异常条件出现的信号。使用try/catch/finally语句,程序员可以找到出错的处理代码,这就简化了出错处理和恢复的任务

4. 安全性

Java的存储分配模型是它防御恶意代码的主要方法之一。Java没有指针,所以程序员不能得到隐蔽起来的内幕和伪造指针去指向存储器。 更重要的是,Java编译程序不处理存储安排决策,所以程序员不能通过查看声明去猜测类的实际存储安排。编译的Java代码中的存储引用在运行时由Java解释程序决定实际存储地址。
Java运行系统使用字节码验证过程来保证装载到网络上的代码不违背任何Java语言限制 。这个安全机制部分包括类如何从网上装载。例如,装载的类是放在分开的名字空间而不是局部类,预防恶意的小应用程序用它自己的版本来代替标准Java类。

5. 支持多线程

Java语言内置了多线程支持:一个Java程序实际上是一个JVM进程,JVM进程用一个主线程来执行main()方法,在main()方法内部,我们又可以启动多个线程。此外,JVM还有负责垃圾回收的其他工作线程等。
Java多线程实现的方式有四种

  1. 继承Thread类,重写run方法
  2. 实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函数的target
  3. 通过Callable和FutureTask创建线程
  4. 通过线程池创建线程

6. 平台无关(可移植性)

https://www.jianshu.com/p/b8ca0164767c

  • JAVA作为一种虚拟的操作系统(OS)和图形用户界面(GUI):操作系统可移植性
    JAVA采用了提供一套与平台无关的库函数(包括AWT、UTIL、LANG等等),库函数提供了一个虚拟的GUI环境。 JAVA程序仅对JAVA库函数提出调用,而库函数对操作系统功能的调用由各不同的虚拟机来完成。JAVA也在它的OS/GUI库中使用了一种"罕见名称符"(least-commom-denominator)来提供对某种特定操作系统的功能调用,即此功能只在特定环境下生效而在其它操作系统下则被忽略。这样做的好处在于可以针对某操作系统生成拥有人们熟悉的界面的应用程序而同时此程序又能在其它系统下运行。缺点则是系统中的某些功能调用有很强的依赖性因而在JAVA的虚拟OS/API中难以实现。
  • JAVA作为一种编程语言:源代码可移植性
    任意一个JAVA程序,不论它运行在何种CPU、操作系统或JAVA编译器上,都将产生同样的结果。 java定义了严密的语意结构, 而使编译器不承担这方面的工作。另外,JAVA对程序的行为的定义也比C和C++严格,如:它提供了内存自动回收功能(GarbageCollection),使程序不能访问越界内存;它对未初始化的变量提供确定值等等。
  • JAVA作为一个虚拟机:CPU可移植性
    大多数编译器产生的目标代码只能运行在一种CPU上。** java先编译成字节码(中间码),由JVM(java虚拟机来解释执行),而这个JVM对于主流的操作系统都有相应的版本,目的就是将 统一的中间码 编译成对应操作系统识的二进制码,然后执行 ** 。

7. 编译解释并存

译型语言是指编译器针对特定的操作系统将源代码一次性翻译成可被该平台执行的机器码;解释型语言是指解释器对源程序逐行解释成特定平台的机器码并立即执行。java先编译成字节码后字节码被java解释器解释执行。
Java编译程序生成字节码(byte-code),而不是通常的机器码。Java程序可以在任何实现了Java解释程序和运行系统(run-time system)的系统上运行。由字节码到机器码过程中 JVM 类加载器首先加载字节码文件,然后通过解释器逐行解释执行,这种方式的执行速度会相对比较慢。而且,有些方法和代码块是经常需要被调用的(也就是所谓的热点代码),所以后面引进了 JIT 编译器,而 JIT 属于运行时编译当 JIT 编译器完成第一次编译后,其会将字节码对应的机器码保存下来,下次可以直接使用。而我们知道,机器码的运行效率肯定是高于 Java 解释器的。这也解释了我们为什么经常会说 Java 是编译与解释共存的语言

8. 支持网络编程

https://www.runoob.com/java/java-networking.html

网络编程:编写运行在多个设备(计算机)的程序,这些设备都通过网络连接起来。Java设计成支持在网络上应用,它是分布式语言。Java既支持各种层次的网络连接,又以Socket类支持可靠的流(stream)网络连接,所以用户可以产生分布式的客户机和服务器。java.net 包中提供了两种常见的网络协议的支持(TCP,UDP)
网络变成软件应用的分布运载工具。Java程序只要编写一次,就可到处运行。

与C++的一些对比

1. go to

把控制无条件转移到同一函数内的被标记的语句。好处:break; 只能跳出单层的循环,return 将整个函数都返回了。go to能从多重循环体中一下子跳到外面,用不着写很多次的break语句 坏处:使得程序的控制流难以跟踪,使程序难以理解和难以修改。

goto label;
..
.
label: statement;
2. C++中的重载(overload)、覆盖(override)、重定义(redefining)

C++ 允许在同一作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符重载。
C++函数重载

  • 同一个类中
  • 函数名相同,参数不同(个数、类型、顺序、名称)
  • 重载解析中不考虑返回类型,而且在不同的作用域里声明的函数也不算是重载。
    C++运算符重载:重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。
    C++覆盖
  • 派生类函数覆盖基类函数(作用域不同)
  • 函数名相同,参数相同,返回值相同,基类函数必须有virtual 关键字
    C++重定义:也叫隐藏。子类重定义父类中的非虚函数,屏蔽了父类的同名函数(相当于创建了一个新的函数,跟父类无关),指派生类类型的对象、引用、指针访问基类和派生类都有的同名函数的时候,访问的是派生类的函数,隐藏了基类同名函数。
  • 子类和父类函数的名称相同,参数也相同,父类中的函数不是virtual,父类的函数将被隐藏
  • 子类和父类的函数名称相同,但参数不同,此时不管父类函数是不是virtual函数,都将被隐藏
3. java中的重载和覆写

java重载

  • 发生在同一个类中或父子类之间
  • 方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同。
  • 编译器通过调用方法使用的值类型和各个重载方法提供的参数类型匹配决定调用哪个函数,如果没有匹配的则产生编译时错误(重载解析过程
  • 返回值不是方法签名即不能有相同名字、相同参数但返回值不同的方法
    java覆盖
  • 覆盖发生在运行期,是子类对父类的允许访问的方法的实现过程进行重新编写
  • 返回值类型、方法名、参数列表必须相同,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类。
  • 如果父类方法访问修饰符为 private/final/static 则子类就不能重写该方法,但是被 static 修饰的方法能够被再次声明。
  • 构造方法无法被重写
  • 如需要调用父类中原有的方法,可以使用super关键字,该关键字引用了当前类的父类。
  • 重写伴随着继承,他的的意义主要在于实现多态,用父类的引用来操作子类对象,但在实际的运行中对象将运行自己重写的方法。但是,如果调用子类特有的方法,编译出错

重载和重写的区别
重写的返回值类型:如果方法的返回类型是 void 和基本数据类型,则返回值重写时不可修改。但是如果方法的返回值是引用类型,重写时是可以返回该引用类型的子类的。

4. java和C++关于重载、覆盖、重定义的一些比较
  • java和C++的重写。why java中没有重定义:Java中重写只要写一个和父类函数同名同参数的同返回值类型的函数即可(这在C++中就是“重定义”),不需要抽象函数(即C艹中的虚函数),因此Java中的普通函数就能起到C艹中虚函数的作用。Java中需要用super关键字来调用父类中的函数(需要参数相同),而C++中需要用作用域解析运算符“::”来调用父类中的方法。
  • C++中虚函数与java抽象:Java中没有虚函数的概念,因为C++的虚函数是用来让子类重写的,然而Java中普通方法就可以被子类重写,因此没有必要用虚函数。Java中的抽象函数相当于C艹中的纯虚函数
5. C++多继承

一个派生类可以有两个或多个父类,从而具备多个父类的特征

  1. 多继承缺点:
  • 若子类继承的父类中拥有相同的成员变量,子类在引用该变量时将无法判别使用哪个父类的成员变量
  • 若一个子类继承的多个父类拥有相同方法,同时子类并未覆盖该方法(若覆盖,则直接使用子类中该方法),那么调用该方法时将无法确定调用哪个父类的方法。
6. java与多继承

https://blog.csdn.net/rocling/article/details/82350515

java中不支持多继承,java实现多继承功能的策略:

  • 多层继承:实际就是多个单继承累积,最下面的子类可以具备前几个父类的特征,但这样的多继承会造成代码冗余,可读性较差
  • 内部类:通过成员内部类实现多继承
/*假如有一个打电话类Call,里面实现了一个可以打电话的功能的方法callSomebody(String phoneNum);
一个发信息类SendMessage,里面实现了一个可以发信息功能的方法sendToSomebody(String phoneNum);
还有一个手机类Phone,这个手机类想实现打电话和发信息的功能;我们知道可以用继承来获得父类的方法,但是只可以单继承呀,也就是说只可以实现其中一个类里面的方法,这并不满足我们的需求。*/
class Call {
	public void callSomebody(String phoneNum){
		System.out.println("我在打电话喔,呼叫的号码是:" + phoneNum);
	}
}
class SendMessage {
	public void sendToSomebody(String phoneNum){
		System.out.println("我在发短信喔,发送给 :" + phoneNum);
	}
}
public class Phone {
//新建内部类extends要继承的类们
	private class MyCall extends Call{
		
	}
	private class MySendMessage extends SendMessage{
		
	}
//new要被继承的类们的对象	
	private MyCall call = new MyCall();
	private MySendMessage send = new MySendMessage();
//自己想要实现的方法实现中用要被继承的对象调用响应要被实现的功能
	public void phoneCall(String phoneNum){
		call.callSomebody(phoneNum);
	}
	
	public void phoneSend(String phoneNum){
		send.sendToSomebody(phoneNum);
	}
	
	public static void main(String[] args) {
		Phone phone = new Phone();
		phone.phoneCall("110");
		phone.phoneSend("119");
	}
}
  • 接口:多继承机制实现优先使用接口,接口使用比较灵活,在企业级项目编程是最推荐的方式
7. java引用与C/C++中的指针

https://www.cnblogs.com/gameoverit/p/5178844.html
https://blog.csdn.net/cewei711/article/details/52881139

C/C++中,指针是指向内存中的地址,该地址就是存储变量的值。该地址所存储的变量值是“公有”的,此处的“公有”是对于拥有该地址的变量而言。它们都可以访问该地址的内容,并且可对其就行修改,一经修改则所有指向该地址的变量值也将改变
Java当中的数据类型只有两种,基本类型、引用类型。除了八种基本类型(int、double、float、char等),剩下的全都是引用类型。而Java的引用实际上是对“指针”的一个封装,是受限指针,不能参与整数运行和指向任意位置的内存,并且不用显示回收对象。

java中内存的分配方式有两种,一种是在堆中分配,一种是在堆栈中分配,所有new出来的对象都是在堆中分配的,函数中参数的传递是在栈中分配的。通常情况下堆的内存可以很大,比如32位操作系统中的虚拟内存都可以被堆所使用(当内存紧张的时候甚至硬盘都可以是堆的存储空间),而堆栈的内存分配是有限的。

//在堆中创建了一个String对象,内容为”string”,在栈中创建了一个引用s,它指向堆中刚创建好的String对象。
String s = new String(“string”);
//引用s值的改变不影响它所指的对象,只有通过它调用对象的方法对可能改变对象的内容。
s = new String(“abc”);//引用's'指向别的对象了,没有改变在堆中"string"这个对象(不考虑垃圾回收情况)

java引用与C++指针的联系:java中的引用和C++中的指针本质上都是想通过一个叫做引用或者指针的东西,找到要操作的目标(变量 对象等),方便在程序里操作。所不同的是JAVA的办法更安全,方便些,但没有了C++的灵活,高效。

java引用简化了C++指针的对应操作:在C++的对象指针里面,出现的指针运算符主要有以下几个:*,->,运算符*是返回指针所指向的对象,而->是返回指针所指向对象的数据成员或方法成员。由于不存在对象变量,而是通过指针来访问对象的,因此Java中不需要提供*运算符,这是Java优化了C++的一个指针问题。对于->运行符,Java的指针是提供的,不过是采用.运算符的方式提供

java引用与C++指针的区别

  • C++中的指针是可以参与和整数的加减运算的,当一个指对指向一个对象数组时,可以通过自增操作符访问该数组的所有元素;并且两个指针能进行减运算,表示两个指表所指向内存的“距离”。而Java的指针是不能参与整数运算和减法运算的。
  • C++中的指针是通过new运算或对象变量取地址再进行赋值而初始化的,可以指向堆中或栈中的内存空间。Java同样是类似的,通new运算得到初始化。Java中指针只能是指向堆中的对象,对象只能生存在堆中。C语言是可以通过远指针来指向任意内存的地址,因而可以该问任意内存地址(可能会造成非法访存);Java中的指针向的内存是由JVM来分配的中,由于new运算来实现,不能随所欲为地指向任意内存
  • C++中通过delete和delete[] 运算符进行释放堆中创建的对象。如果对象生存周期完结束,但没有进行内存释放,会出现内存泄露现象。在Java中,程序员不用显式地释放对象,垃圾回收器会管理对象的释放问题,不用程序员担心。Java使用了垃圾回收机制使得程序不用再管理复杂的内存机制,使软件出现内存泄露的情况减少到最低。即Java的引用不用关心内存回收的问题
8. C++中的引用和指针

C++的对象类型分为三种:对象变量,对象指针和对象引用(这里特指是C++的引用)。对象变量,与基本数据类型变量一样,分配在栈中,对象在栈的生命空间结束后,系统会自动释放对象所占用的空间;对象指针,与C语言中的指针一样,都是一个地址,它指向栈中或堆中的一个对象。对象引用是C++中与C不同之外,形象地说,引用就是一个别名,定义引用的时候必须一起初始化,不能引用不存在的对象。

引用:是别名,int &n=mn 既不是m的拷贝,也不是指向 m 的指针,其实n就是 m 它自己。

指针:是一个变量,存储的是一个地址,指向内存的一个存储单元;

引用的规则

  • 引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)
  • 不能有 NULL 引用,引用必须与合法的存储单元关联(指针则可以是 NULL)
  • 一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)
  • 指针可以有多级,引用只能是一级
    C++中引用的功能:传递函数的参数和返回值。
9. C++中函数参数和返回值传递方式

C++ 语言中,函数的参数和返回值的传递方式有三种:值传递、指针传递和引用传递。

  1. 按值传递
//Func1 函数体内的 x 是外部变量 n 的一份拷贝,改变 x 的值不会影响 n, 所以 n 的值仍然是 0。
void Func1(int x) 
{ 
    x = x + 10; 
} 
... 
int n = 0; 
Func1(n); 
cout << "n = " << n << endl; // n = 0 
  1. 指针传递
// Func2 函数体内的 x 是指向外部变量 n 的指针,改变该指针的内容将导致 n 的值改变,所以 n 的值成为 10。
void Func2(int *x) 
{ 
    (* x) = (* x) + 10; 
} 
... 
int n = 0; 
Func2(&n); 
cout << "n = " << n << endl; // n = 10 
  1. 引用传递
// Func3 函数体内的 x 是外部变量 n 的引用,x 和 n 是同一个东西,改变 x 等于改变 n,所以 n 的值成为 10。
void Func3(int &x) 
{ 
    x = x + 10; 
} 
... 
int n = 0; 
Func3(n); 
cout << "n = " << n << endl; // n = 10

“引用传递"的性质象"指针传递”,而书写方式象"值传递"。“引用"可以做的任何事情"指针"也都能够做,为什么还要"引用"这东西?答案是"用适当的工具做恰如其分的工作”。指针能够毫无约束地操作内存中的任何东西,尽管指针功能强大,但是非常危险。

10. 内存模型

https://blog.csdn.net/m0_37872413/article/details/88830124

JVM讲内存划分为了5大模块:1、方法区。2、堆。3、JVM栈。4、本地方法栈。5、程序计数器(这5大区域里面,线程私有的是3,4,5这三个模块(即线程自己拥有的空间,其他线程不能访问到的),而线程共享(就是所有线程都能够访问的数据)的则是1和2)

  • 堆: 存放了几乎所有程序中new出来的对象实例(new出来的对象,都在堆中分配内存,当然,在最新的jdk版本中,这已经不是绝对正确的了)。因为几乎所有的对象实例都在这里分配,所以GC收集器回收的内存大部分都是从这里回收
  • 栈: 描述的是Java方法执行时的内存模型。每一个方法在执行的同时,都会在JVM栈中创建一个栈帧,用于存储局部变量表等各种数据,方法中所有你用到的变量都会在这里找到(基本数据类型,如int,long,double这种,就是保存的变量的值,而像对象,String这种引用类型的变量,它保存的并不是值,而是一个地址,这个地址就指向真实的数据)。所有new出来的对象都在堆中分配内存,而栈又是用于存储方法的局部变量等数据的,那如果在方法里面,new了一个局部变量出来,这个变量其实还是在堆中分配的内存,但是,会在栈中存放这个变量的地址,该地址就指向堆中这个变量分配的内存地址。

java中数据分为基本数据类型和引用类型,基本类型变量存储在Java栈(VM Stack)中,引用变量存储在堆(Heap)中

11. java中参数传递方式

https://www.cnblogs.com/sum-41/p/10799555.html
https://blog.csdn.net/a41664256/article/details/78948137
https://www.cnblogs.com/lixiaolun/p/4311863.html

形式参数:是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数,简称“形参”。

实际参数:在主调函数中调用一个函数时,函数名后面括号中的参数称为“实际参数”,简称“实参”

java中数据分为基本数据类型和引用类型

值传递:实际参数把它的值传递给对应的形式参数,函数接收的是原始值的一个copy,此时内存中存在两个相等的基本类型,即实际参数和形式参数,后面方法中的操作都是对形参这个值的修改,不影响实际参数的值。

引用传递:传过来的就是这个引用参数的副本,这个副本存放的是参数的地址。 如果在函数中没有改变这个副本的地址,而是改变了地址中的 值,那么在函数内的改变会影响到传入的参数。如果在函数中改变了副本的地址,如new一个,那么副本就指向了一个新的地址,此时传入的参数还是指向原来的 地址,所以不会改变参数的值。

java中只有值传递:java中引用类型数据的传递复制的是参数的引用(地址值),并不是引用指向的存在于堆内存中的实际对象。

特殊考虑String,以及Integer、Double等基本类型包装类,它们的类前面都有final修饰,为不可变的类对象,每次操作(new或修改值)都是新生成一个对象,对形参的修改时,实参不受影响。

基本类型的值传递:每个方法的形参,也都相当于是一个局部变量,它也会在栈中分配一个内存用来保存它的值。当你将方法外部的实参传递给形参的时候,对于基本数据类型,就相当于是给形参这个变量赋值了,将他内存的数据修改为你实参一样的数据了,然后在方法内部对形参进行操作时,其实操作的都是这个形参的内存,并没有操作到实参的内存,如下图所示:

基本类型值传递

引用类型的值传递

  • 引用类型值传递改变不实际对象(如对象某属性)的情况:对象、数组,或者String这种类型,实参和形参其实存放的是对象的内存地址(对象在堆中分配,所以这个地址指向堆中的某个位置),并没有保存实参的数据的值。这个内存地址指向了对象的首地址。所以,当在方法体内部用“=”给形参(即一个地址的副本)赋值时,其实真正的操作是,修改了形参存放的内存地址,所以形参就指向了另外一个对象,但是实参,它还是指向的原来的对象。
Class User{
	private String name;
	public String getName(){
		return name;
	}
	public String setName(String name){
	this.name = name; 
	}
public class RefUnhangedUser{
	public static void main(String[] args){
		RefUnchanged  rn = new RefUnchanged();
		User user = new User();
		user.setName("张三");
		rs.changeName(user);
		//此处user.getName()="张三";
		
	}
	public void changeName(User user){
	//方法被调用时,先复制了传递过来的引用(地址值)生成了一个地址副本指向堆中的user对象,但是方法里随即执行user = new User();改变了引用的副本值即这个引用副本(地址)不再指向原来的堆中的user对象了 ,而是指向新在堆中new了的对象 这个对象的name属性是李四。 原本做参数传递的引用仍指向原堆中的user对象,这个user对象的name仍然是张三
		user = new User();
		user.setName("李四");
	}
}
  • 引用类型值传递改变实际对象的情况:形参的值就是该对象的引用(一个地址)的一个副本。副本地址和原引用指向同一个对象,此时通过副本地址改对象属性,对象的内容可以在被调用的方法中改变。
Class User{
	private String name;
	public String getName(){
		return name;
	}
	public String setName(String name){
	this.name = name; 
	}
//改变了引用指向的对象的实际内容   
public class RefChangedUser{
	public static void main(String[] args){
		RefUnchanged  rn = new RefUnchanged();
		User user = new User();
		user.setName("张三");
		rn.changeName(user);
		//此处user.getName()="李四";
		
		
	}
	public void changeName(User user){
	//传递一个对象,是引用类型数据,方法执行时复制了一个同样指向堆中user这个对象的地址副本,通过地址副本改变了user这个对象的name值。最终user的name="李四"
		user.setName("李四");
	}
}		

1.5 java和C++异同总结

  • 相同点
  1. 都是面向对象的语言,都使用了面向对象的思想(封装、继承、多态),重用性好
  • 不同点
  1. 指针:java无指针,但有引用
  2. 继承:java单继承不支持多继承但java可以用多层继承、接口、内部类实现多继承的功能
  3. 自动内存管理:java自动进行无用内存回收操作C++中必须由程序释放内存资源
  4. 重载:Java 不支持操作符重载,操作符重载被认为是 C++的突出特征。
  5. 预处理:C++语言支持预处理,而Java语言没有预处理器。Java虽然不支持预处理功能(头文件、宏定义等),但它提供的import机制和C++中的预处理器功能类似。
  6. 解释型/编译型语言: C++源码一次编译,直接在编译的过程中链接了,形成了机器码。Java源码会先经过一次编译,成为中间码,中间码再被解释器解释成机器码。对于Java而言,中间码就是字节码(.class),而解释器在JVM中内置了。因此,Java的执行速度比C/C++慢,但是Java能够通过JVM跨平台执行,而C/C++不能。
  7. java无goto语句,有goto关键字但不支持使用
  8. 类型转换:Java 不支持 C++中的自动强制类型转换,如果需要,必须由程序显式进行强制类型转换。java运行时对类型检测,如果类型不正确会抛出ClassCastException异常

2. JVM、JDK、JRE

1. JVM

Java 虚拟机(JVM)是运行 Java 字节码的虚拟机。

字节码:JVM 可以理解的代码(.class文件) java先编译成字节码(中间码),由JVM(java虚拟机来解释执行),而这个JVM对于主流的操作系统都有相应的版本,目的就是将 统一的中间码 编译成对应操作系统识的二进制码,然后执行 。

java程序源码到运行的过程:
java代码运行过程

Java程序可以在任何实现了Java解释程序和运行系统(run-time system)的系统上运行。由字节码到机器码过程中 JVM 类加载器首先加载字节码文件,然后通过解释器逐行解释执行,这种方式的执行速度会相对比较慢。而且,有些方法和代码块是经常需要被调用的(也就是所谓的热点代码,二八定律),所以后面引进了 JIT 编译器,而 JIT 属于运行时编译当 JIT 编译器完成第一次编译后,其会将字节码对应的机器码保存下来,下次可以直接使用。而我们知道,机器码的运行效率肯定是高于 Java 解释器的。这也解释了我们为什么经常会说 Java 是编译与解释共存的语言。

2. JDK 和 JRE

  • JDK :java开发工具,包含JRE ,还有编译器(javac)和工具(如 javadoc 和 jdb)。它能够创建和编译程序。
  • JRE : Java 运行时环境。它是运行已编译 Java 程序所需的所有内容的集合,包括 Java 虚拟机(JVM),Java 类库,java 命令和其他的一些基础构件。但是,它不能用于创建新程序。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值