java基础相关,提纲参考javaguide
阅读提示:参考紧随标题
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
https://zhuanlan.zhihu.com/p/32221103
把C++语言中一些复杂特征去掉,eg Java不支持go to语句,代之以提供break和continue语句以及异常处理。Java还剔除了C++的操作符过载(overload)和多继承特征,并且不使用主文件,免去了预处理程序。因为Java没有结构,数组和串都是对象,所以不需要指针。Java能够自动处理对象的引用和间接引用,实现自动的无用单元收集gc,使用户不必为存储管理问题烦恼。
面向对象使用类和对象实现
https://www.cnblogs.com/chenssy/p/3351835.html
继承、封装、多态
继承是使用已存在的类的定义作为基础建立新类,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。快速地创建新的类,可以提高代码的重用,程序的可维护性,节省大量创建新类的时间 ,提高我们的开发效率。java只单继承
把一个对象的状态信息(也就是属性)隐藏在对象内部,不允许外部对象直接访问对象的内部信息。但是可以提供一些可以被外界访问的方法来操作属性
https://www.cnblogs.com/x54256/p/8410142.html
封装体现:属性的封装、方法的封装、类的封装、组件的封装、模块化封装、系统级封装…
封装优点
多态的含义:指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定。
不修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变。相同消息不同类做出不同响应。
对于面向对象而言,多态分为编译时多态和运行时多态。其中编辑时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的函数,通过编辑之后会变成两个不同的函数,在运行时谈不上多态。而运行时多态是动态的,它是通过动态绑定来实现的,也就是我们所说的多态性。
向上转型:父类类型的引用可以调用父类中定义的所有属性和方法,子类特有的属性和方法不能访问。子类的重载(函数名同参数不同)父类的方法也不能被调用(属于子类特有,向上转型为父类引用时丢失),只能调用父类的被重载的方法;子类覆盖的父类的方法调用的一定是子类的这个方法。
多态性体现在两方面:方法的重载和覆盖、对象的多态性
对象多态:父类可以有不同的子类,向上转型:将子类实例转为父类引用自动,向下转型:将父类实例转为子类实例强制转换,类型不对报异常
java实现多态有三个必要条件:继承、覆盖、向上转型。
继承:在多态中必须存在有继承关系的子类和父类。
覆盖:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
向上转型:在多态中子类对象的引用是父类,只有这样该引用才能够具备技能调用父类的方法和子类的方法
多态实现方法
instanceof关键字
我们可以通过instanceof关键字来判断某个对象是否属于某种数据类型。如学生的对象属于学生类,学生的对象也属于人类。
Java是一个强类型语言,它允许扩展编译时检查潜在类型不匹配问题的功能。Java要求显式的方法声明,它不支持C风格的隐式声明。可靠性方面最重要的增强之一是Java的存储模型。Java不支持指针,它消除重写存储和讹误数据的可能性。类似地,Java自动的“无用单元收集”预防存储漏泄和其它有关动态存储分配和解除分配的有害错误。Java解释程序也执行许多运行时的检查,诸如验证所有数组和串访问是否在界限之内。异常处理是Java中使得程序更稳健的另一个特征。异常是某种类似于错误的异常条件出现的信号。使用try/catch/finally语句,程序员可以找到出错的处理代码,这就简化了出错处理和恢复的任务
Java的存储分配模型是它防御恶意代码的主要方法之一。Java没有指针,所以程序员不能得到隐蔽起来的内幕和伪造指针去指向存储器。 更重要的是,Java编译程序不处理存储安排决策,所以程序员不能通过查看声明去猜测类的实际存储安排。编译的Java代码中的存储引用在运行时由Java解释程序决定实际存储地址。
Java运行系统使用字节码验证过程来保证装载到网络上的代码不违背任何Java语言限制 。这个安全机制部分包括类如何从网上装载。例如,装载的类是放在分开的名字空间而不是局部类,预防恶意的小应用程序用它自己的版本来代替标准Java类。
Java语言内置了多线程支持:一个Java程序实际上是一个JVM进程,JVM进程用一个主线程来执行main()方法,在main()方法内部,我们又可以启动多个线程。此外,JVM还有负责垃圾回收的其他工作线程等。
Java多线程实现的方式有四种
https://www.jianshu.com/p/b8ca0164767c
译型语言是指编译器针对特定的操作系统将源代码一次性翻译成可被该平台执行的机器码;解释型语言是指解释器对源程序逐行解释成特定平台的机器码并立即执行。java先编译成字节码后字节码被java解释器解释执行。
Java编译程序生成字节码(byte-code),而不是通常的机器码。Java程序可以在任何实现了Java解释程序和运行系统(run-time system)的系统上运行。由字节码到机器码过程中 JVM 类加载器首先加载字节码文件,然后通过解释器逐行解释执行,这种方式的执行速度会相对比较慢。而且,有些方法和代码块是经常需要被调用的(也就是所谓的热点代码),所以后面引进了 JIT 编译器,而 JIT 属于运行时编译。当 JIT 编译器完成第一次编译后,其会将字节码对应的机器码保存下来,下次可以直接使用。而我们知道,机器码的运行效率肯定是高于 Java 解释器的。这也解释了我们为什么经常会说 Java 是编译与解释共存的语言
https://www.runoob.com/java/java-networking.html
网络编程:编写运行在多个设备(计算机)的程序,这些设备都通过网络连接起来。Java设计成支持在网络上应用,它是分布式语言。Java既支持各种层次的网络连接,又以Socket类支持可靠的流(stream)网络连接,所以用户可以产生分布式的客户机和服务器。java.net 包中提供了两种常见的网络协议的支持(TCP,UDP)
网络变成软件应用的分布运载工具。Java程序只要编写一次,就可到处运行。
把控制无条件转移到同一函数内的被标记的语句。好处:break; 只能跳出单层的循环,return 将整个函数都返回了。go to能从多重循环体中一下子跳到外面,用不着写很多次的break语句 坏处:使得程序的控制流难以跟踪,使程序难以理解和难以修改。
goto label; .. . label: statement;
C++ 允许在同一作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符重载。
C++函数重载:
java重载
重写的返回值类型:如果方法的返回类型是 void 和基本数据类型,则返回值重写时不可修改。但是如果方法的返回值是引用类型,重写时是可以返回该引用类型的子类的。
一个派生类可以有两个或多个父类,从而具备多个父类的特征
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"); } }
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++的对象类型分为三种:对象变量,对象指针和对象引用(这里特指是C++的引用)。对象变量,与基本数据类型变量一样,分配在栈中,对象在栈的生命空间结束后,系统会自动释放对象所占用的空间;对象指针,与C语言中的指针一样,都是一个地址,它指向栈中或堆中的一个对象。对象引用是C++中与C不同之外,形象地说,引用就是一个别名,定义引用的时候必须一起初始化,不能引用不存在的对象。
引用:是别名,int &n=m
n 既不是m的拷贝,也不是指向 m 的指针,其实n就是 m 它自己。
指针:是一个变量,存储的是一个地址,指向内存的一个存储单元;
引用的规则:
C++ 语言中,函数的参数和返回值的传递方式有三种:值传递、指针传递和引用传递。
//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
// Func2 函数体内的 x 是指向外部变量 n 的指针,改变该指针的内容将导致 n 的值改变,所以 n 的值成为 10。 void Func2(int *x) { (* x) = (* x) + 10; } ... int n = 0; Func2(&n); cout << "n = " << n << endl; // n = 10
// 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
“引用传递"的性质象"指针传递”,而书写方式象"值传递"。“引用"可以做的任何事情"指针"也都能够做,为什么还要"引用"这东西?答案是"用适当的工具做恰如其分的工作”。指针能够毫无约束地操作内存中的任何东西,尽管指针功能强大,但是非常危险。
https://blog.csdn.net/m0_37872413/article/details/88830124
JVM讲内存划分为了5大模块:1、方法区。2、堆。3、JVM栈。4、本地方法栈。5、程序计数器(这5大区域里面,线程私有的是3,4,5这三个模块(即线程自己拥有的空间,其他线程不能访问到的),而线程共享(就是所有线程都能够访问的数据)的则是1和2)
java中数据分为基本数据类型和引用类型,基本类型变量存储在Java栈(VM Stack)中,引用变量存储在堆(Heap)中
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或修改值)都是新生成一个对象,对形参的修改时,实参不受影响。
基本类型的值传递:每个方法的形参,也都相当于是一个局部变量,它也会在栈中分配一个内存用来保存它的值。当你将方法外部的实参传递给形参的时候,对于基本数据类型,就相当于是给形参这个变量赋值了,将他内存的数据修改为你实参一样的数据了,然后在方法内部对形参进行操作时,其实操作的都是这个形参的内存,并没有操作到实参的内存,如下图所示:
引用类型的值传递:
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("李四"); } }
Java 虚拟机(JVM)是运行 Java 字节码的虚拟机。
字节码:JVM 可以理解的代码(.class文件) java先编译成字节码(中间码),由JVM(java虚拟机来解释执行),而这个JVM对于主流的操作系统都有相应的版本,目的就是将 统一的中间码 编译成对应操作系统识的二进制码,然后执行 。
java程序源码到运行的过程:
Java程序可以在任何实现了Java解释程序和运行系统(run-time system)的系统上运行。由字节码到机器码过程中 JVM 类加载器首先加载字节码文件,然后通过解释器逐行解释执行,这种方式的执行速度会相对比较慢。而且,有些方法和代码块是经常需要被调用的(也就是所谓的热点代码,二八定律),所以后面引进了 JIT 编译器,而 JIT 属于运行时编译。当 JIT 编译器完成第一次编译后,其会将字节码对应的机器码保存下来,下次可以直接使用。而我们知道,机器码的运行效率肯定是高于 Java 解释器的。这也解释了我们为什么经常会说 Java 是编译与解释共存的语言。