发表时间:2022-03-25来源:网络
Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程 。
1.简单性
java可以说是C++的纯净版,没有头文件,没有指针运算,而且语法基于C,学习起来相比C++和C会比较轻松一点。
2.面向对象
了解面向对象之前,我们先了解一下面向过程。两种都是解决问题的一种思想。
面向过程思想
步骤清晰且具体化,思考问题都放在每一步具体的实现上,第一步做什么,第二步。比如你要去上个厕所,得先站起身,走出门,进厕所,脱下裤子,开吃。他适合处理一些简单的问题,但是如果你用这种思想去解决一些相对复杂的问题,比如你要造一座高楼,你一个人去造这个高楼,可能也能造出来,可是相对的代码的复杂程度以及耦合度可能会很高,也相对的难以解决,毕竟一个人要去做所有事情在整个过程里。
面向对象思想
而面向对象思维,是一种分类的思维方式来解决问题,同样以建造一个高楼举例,我会找一个总设计师,总设计分配任务给下面的人,比如做地基的人,装修的人,刷墙的人等等,相当于把这个过程进行细分,刷墙的人可能还会继续往下细分成刷外墙的人和刷内墙的人,而细分出来类型的人,比如刷外墙的人所做的事情,其实也就是一个面向过程的实现。但是总体的架构可以通过这种思想变得非常的清楚。思考问题首先是先思考这个问题应该怎么分类,然后对这些分类进行单独的思考,还是以上厕所为例子,人本身就是一个对象,他有一个方法叫做上厕所,至于中间的拉屎过程在上厕所这个方法里面进行具体的面向过程的实现就好了。这样也易于分工合作,可将每个分类出来的单独思考分配给不同的人,共同完成。面向对象适合处理复杂的问题,适合处理需要多人协作的问题,这也是为什么java能做出那么多大型系统软件的一个核心,能多人一起共同解决。具体的讲解及结合代码的讲解下面会总结
3.可移植性(平台无关性)
所谓平台无关性就是说我的一份代码写好之后,可以在不同的硬件平台上运行,也就是跨平台性。平台无关性有两种:源码级别和目标代码级别的
源码级别
C/C++语言是源码界别的。这两种语言属于编译型语言。只要有源代码,在不同平台上重新进行编译就可以实现跨平台运行。但是你在交付项目代码的时候,我们一般是交付软件产品,不会把源码交出去,防止自己产品的核心代码被泄露,那这个时候,这种软件就只能在特定的平台上运行了
目标代码级别、
Java是目标代码级别的,因为java是一种解释型语言,写好的java代码编译会形成字节码文件,去到哪个平台都能用,只要在那个平台安装JVM虚拟机,虚拟机会根据你所在的平台,选择不同平台的解释器把Java编译的字节码解释为不同的机器码,达到在不同平台运行的效果。一次编译,到处运行
在《JAVA核心技术卷一:基础知识》中描述到:“JAVA并不只是一种语言。在此之前出现的那么多种语言也没用能够引起那么大的轰动。Java是一个完整得平台,有一个庞大的库,其中包含了许多可重用的代码和一个提供诸如安全性、跨操作系统得可以执行以及自动垃圾收集等服务的执行环境。”
关于反编译
所谓反编译就是可以根据class文件反向解析成原来的java文件。因为使用javac编译不像C/C++编译时那样进行加密和混淆,它是直接对其进行符号化、标记化的编译处理,所以很轻易的就能解析出来。
java从出现开始,就十分提倡开放共享,这也是java迅速发展壮大深受广大程序猿喜爱的一个原因。但许多时候,公司为了商业技术的保密考虑等等情况,会不希望可以直接反编译成源码出现源码泄露的情况。那么就需要进行加密措施,这种情况采取的手段也很多:代码混淆,加class(ClassLoader),高级加密class,甚至是直接修改JVM,这些有兴趣的可以百度了解下。
4.高性能
Java三高,我就不多描述了。我想介绍一下东西,叫做即时编译器(JIT),它甚至可以在执行的时候提高性能。
当JVM的初始化完成后,类在调用执行过程中,执行引擎会把字节码转为机器码,然后在操作系统中执行。在字节码转换为机器码的过程中,虚拟机还存在一道编译,那就是即时编译。最初,JVM中的字节码由解释器完成编译,当虚拟机发现某个方法或者是代码块的运行特别频繁,就会把这些代码认定为热点代码,那么为了提高这些代码二执行效率,在运行的时候,JIT会把这些代码编译成和本地平台相关的机器码,然后经过一系列的优化之后,保存到内存中,以此提高执行代码的性能,所以如何去编写代码和设计代码,其实考虑的东西随着学习的知识越来越多,会慢慢的需要考虑的更多,希望终有一天我们都能写出性能极佳,一眼看出代码背后的所有故事。那个时候希望我们可以一起拉屎聊代码。
5.分布式
java可以说是为网络分布式环境所设计的,因为它能够处理很多TCP/IP协议上的东西。除此之外,Java还有一个东西叫RMI(远程方法调用),考虑一个问题,在一个分布式的环境,如果一台机子上的对象调用了另外一台机子上的对象的方法,该如何解决,这还不是另外一台机子上的资源,而是另外一台机子上的虚拟机里的代码?我就不多说了,因为我也不会啊,好奇的可以去了解一下。
6.动态性
java是静态语言,但是因为反射机制,java变成了一个准动态语言,这也是c++所没有的东西。什么叫动态语言,就是在程序运行时,可以改变程序结构或变量类型。而反射机制是指可以在运行时加载使用编译期里完全未知的类,因为在类刚开始加载的时候,堆中就已经产生了一个Class类型的对象,这个对象相当于是一个模板类,里面包含了一个类的完整的类结构信息,且一个类只能有一个Class的对象,而通过这个Class对象,我们就能得到这个类的方法和属性等等,像一面镜子一样,所以我们称它为反射,获取到了这个Class我们就可以进行动态的创建实例调用方法修改和查看属性等等操作了,具体的反射会在下面进行详细说明。
7.多线程
可以并行执行多项任务,可以带来更好的交互行为和事实行为,这就不多说了,线程下面也会详细说明。
8.健壮性
Java语言的强类型机制、异常处理、垃圾的自动收集等
9.安全性
java提供了一个安全沙箱来保证网络或者其他不信任的地方下载并运行程序不会破坏到本地数据,java的安全模型主要从jvm对外的防护,防止jvm被恶意侵入获取特定权限以及对jvm内部的防护。具体东西太多了,推一篇博客。
https://blog.csdn.net/shendeguang/article/details/8213960
JavaSE :标准版(桌面程序,控制台开发) SE是整个java的核心和基础。
JavaME: 嵌入式开发(手机,小家电)
JavaEE: 企业级开发(web,服务器端开发) EE运用的目前最为广泛,它提供了很多企业级应用开发的完整的解决方案。

在计算机的高级语言类型,分为两种:编译型和解释型。
什么是编译型和解释型?比如现在有一个外国人要读一本刘慈欣写的《三体》,但是这个外国人看不懂中文,那么他可以选择买一本把整个三体编译成英文的一本书,而这种就是编译型,编译成你看的懂得语言,你也可以选择请一个翻译,你读到哪里,我给你翻译到哪里,解释给你听,这种就是解释型。相对于计算机而言编译型就是我现在用java这个高级语言,我需要编译成计算机能够直接读懂的语言(其实就是机器语言),让计算机直接去执行。或者选择我一句一句把我写的代码解释给计算机听,然后计算机一句一句去执行。编译型的好处在于,我不需要你给我解释我能更快更简单的读懂执行,所以对速度要求比较高的例如操作系统之类的,一般就会选用编译型语言去做。而解释型语言的特点在于灵活,比如外国人看的那本翻译的书有问题,如果作者想去改变,那么整本书相当于要重新翻译发布,而你如果选择找个人解释翻译给你听,它可以直接指出更改的内容,而Java是属于即是编译型又是解释型的语言。
Java程序运行的机制如图
如上图所示,我们所写的代码也就是源文件,会先经过一次编译,也可以理解为预编译,生成了一个class的字节码文件,这个字节码文件是介于java代码和机器码中间的,然后将这个class文件放入jvm中,在jvm中经过装载校验,然后jvm会通过解释器解释给操作系统也就是计算机听,然后进行执行。
如图所示的代码,在idea里面选择Tools——>Generate JavaDoc
完成之后可以在输出路径看到如下文件,点击index进主页(一般生成了之后会自动弹出主页的)
可以和jdk帮助文档进行对比,如果用心做一个项目,可以考虑维护这种帮助文档,jdk帮助文档如下。
标识符命名规范
所有的标识符都应该以字母A-Z或者a-z,美元符$或者下划线_开始
首字符之后可以是字母,美元符,下划线或者数字的任何字符的组合。
不能使用关键字作为标识符
标识符是大小写敏感的(意思是比如关键字class,你取个名字叫Class,cLass,CLAss也是可以的,但是一般不建议那么做)
可以使用中文名,但是一般并不建议去使用,更不建议使用拼音命名,取名字最好就是见名知意,增加可读性。
java是强类型语言,一般也叫做强类型定义语言,要求变量的使用严格符合规定,对于每一种数据都定义了明确的具体的数据类型,在内存中分配了不同大小的内存空间
基本数据类型
浮点数使用问题
浮点数是不精确的,有涉及小数的操作,最好完全避免使用浮点数进行比较,尤其是银行业务!原因大家来看下面这段代码
为什么浮点数不精确,其实这句话本身就不精确,相对精确一点的说法是:我们程序员在程序里写的十进制的小数,计算机内部无法用二进制的小数来精确的表达。
对于二进制小数,小数点右边能表达的值是1/2,1/4,1/8…1/(2^n)
比如我现在来表达一下十进制的0.2
0.01 = 1/4 = 0.25,太大了
0.001 =1/8= 0.125,又太小了
0.0011=1/8+1/16=0.1875,接近0.2了
0.00111 = 1/8 + 1/16 + 1/32 = 0.21875 , 又大了
0.001101 = 1/8+ 1/16 + 1/64 = 0.203125 还是大
0.0011001 = 1/8 + 1/16 + 1/128 = 0.1953125 这结果不错
0.00110011 = 1/8+1/16+1/128+1/256 = 0.19921875 哇这个好逼近啊 决定就是他了
这就是所说的二进制小数无法精确表示十进制的小数,关于小数的精确计算,推荐使用BigDecimal来做精确运算。
类型转换
这里为什么要把char也放进来呢,因为char实际上也是一个数字,所有的字符本质上还是数字,如下图代码
public static void main(String[] args) { char c1='c'; System.out.println(c1); System.out.println((int)c1); char c2='国'; System.out.println(c2); System.out.println((int)c2); }char字符有一张Unicode编码表,每一个数字对应了表中的一个字符,所以字符的本质其实还是数字,正常情况下还有一种\u0000的写法,\u代表的是转译的意思
类型转换注意点
引用数据类型讲个鬼。
具体的变量的区别,会在后面内存分析的时候再说明
变量的命名规范
所有的变量,方法名,类名:见名知意
类成员变量:首字母小写和驼峰原则:monthSalary
局部变量:首字母小写和驼峰原则
常量:大写字母和下划线:MAX_VALUE
类名:首字母大写和驼峰原则:GoodMan
方法名:首字母小写和驼峰原则:runRun()
运算时类型问题
public static void main(String[] args) { byte b=20; short s=20; int i=20; long l=20; System.out.println(b+s+i+l);//只要运算中有long类型,则返回long类型 System.out.println(b+s+i);//只要运算中有int类型(没有long类型),则返回int类型 System.out.println(b+s);//只要运算中没有long和int类型,则返回int类型 }
短路运算
计算机在进行运算的时候如果提前确认了结果,会直接将语句短路,执行下一条,比如boolean flag=(c
char c='B';
switch (c){
case 'A':
System.out.println(13);
break;
case 'B':
System.out.println(13);
case 'C':
System.out.println(14);
case 12:
System.out.println(15);
}
//输出结果为 13 14 15
}
注意点:如果我在匹配一个String字符串的时候,传入了一个空的字符串,会发生什么事,会报错吗?
public static void main(String[] args) { String c=null; switch (c){ case "A": System.out.println(13); break; case "B": System.out.println(13); case "C": System.out.println(14); case "12": System.out.println(15); } } 会报错,会直接报NullPointerException错误,为什么会报错,我们可以把这个代码生成的class文件进行反编译,可以得到如下代码。 public static void main(String[] args) { String c = null; byte var3 = -1; switch(((String)c).hashCode()) { case 65: if (((String)c).equals("A")) { var3 = 0; } break; case 66: if (((String)c).equals("B")) { var3 = 1; } break; case 67: if (((String)c).equals("C")) { var3 = 2; } break; case 1569: if (((String)c).equals("12")) { var3 = 3; } } switch(var3) { case 0: System.out.println(13); break; case 1: System.out.println(13); case 2: System.out.println(14); case 3: System.out.println(15); } } 可以发现,他们去匹配这个字符串的时候,实际上匹配的是hashcode的值,所以将null进行hascode方法的时候自然会报NullPointerExceptionwhile必须满足条件,条件表达式只要一直为true就一直执行。
dowhile至少执行一次
break强制退出整个循环,continue跳过本次循环,如果有多层循环想一次性跳出,可以在外面的循环语句前定义一个标号,代码如下
Java方法是语句的集合,它们在一起执行一个功能。比如人是一个类,上厕所是人的一个方法。
设计方法的原则:方法的本意是功能块,就是实现某个功能的语句块的集合。我们设计方法的时候,最好保持方法的原子性,也就是一个方法只完成一个功能,这样利于我们后期的扩展。
return会直接终止方法。
方法调用有两种方式,根据方法是否返回值来选择,当方法有返回值的时候,通常被当作一个值。如果方法返回void,方法调用一定是一条语句。
那么,方法中的参数是引用传递还是值传递呢?
介绍一下形参和实参
形参是指定义函数名和函数体所用的参数。public void test(String a)
实际参数指调用函数填入括号内的参数。test(“字符串”)。两个之间存在拷贝关系。
test方法里进行了赋值操作,main方法的a,b没有变化,这也说明了这是一个拷贝的数值,并不是直接修改了源数值。他是将入参复制了一份,进行操作的,完全不影响源数值。那么引用呢,test2就是一个传入test的对象引用,输出结果修改值成功了,那是不是java即是值传递,也是引用传递呢,其实还是只是一个值传递,因为他只不过是复制了你得地址位置。你得地址相当于一把钥匙的话,那么复制你得地址相当于是复刻了一把新的钥匙,这不能算是引用传递,你并没有把原本的钥匙给我,所以,值传递和引用传递的区别并不是传递的内容。而是实参到底有没有被复制一份给形参。
还有一个点就是上面的String也是引用类型,为什么没有改变值,正常来说引用类型应该是传入了一个地址的。因为地址的Test类对象地址输出实际上是toString方法的输出,但是String内部重写了toString方法,输出的不会是地址,而会是值,相当于把输出的值传递进了方法里进行拷贝,这也从侧面反映了java确实是值传递,并没有将引用传递。
重载方法就是在一个类中,有相同的函数名称,但形参不同的函数。
方法重载的规则:
方法名称必须相同。
参数列表必须不同(个数不同,类型不同,参数排列顺序不同)。
方法的返回类型可以相同可以相同。
仅仅返回类型不同无法构成方法的重载。
方法名称相同的时候,编译器会根据调用方法的参数个数,参数类型等去逐个匹配,已选择对应方法,如果匹配失败,编译器报错。
这是一个简单的递归,计算的4的阶乘,过程分析如下
深入的递归就不说了,因为还在复习基础,如果有兴趣深入的,阶乘、斐波那契数列、汉诺塔、杨辉三角的存取、字符串回文判断、字符串全排列、二分查找、树的深度求解八个经典问题都可以去了解一下。
数组的长度是确定的,数组一旦被创建,它的大小就不可以改变
数组元素必须是相同类型,不可以出现混合类型。
数组中的元素可以是任何数据类型,包括基本类型和引用类型。
数组变量属于引用类型,数组也可以看成对象,数组中的每个元素相当于该对象的成员变量。
数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中的。
当声明int[] array;的时候,array会被压入栈内,此时array的值是空的,只有当array=new int[5];的时候,因为数组也是一个引用类型,或者说是一个对象,会在堆中开辟一个array数组的空间,这里定义的数组长度为5,一旦定义长度之后不可改变,数组在这个时候会进行默认初始化,也就是int的数组,会赋值给数组每一个元素int类型的默认值0。

下列是初始化的三种方式
二维数组就和套娃一样,多维数组就是无限套娃。二维数组就是一个数组里的每一个元素都是一个数组,所以你在int[][]用new初始化的时候可以先不分配元素上的数组的长度,也就是可以int[][] a=new[10][]new还是一样会赋给每个元素初始值,每个元素都是数组,数组是引用类型,初始值为null。
比较相邻的两个元素,如果第一个比第二个大,就移到后面去,假设冒泡从数组第一个元素开始进行比较,一直比较到最后一个,那么必定会产生一个数组的最大值,如果重复这个过程,遍历情况最差的时候应该是重复遍历了数组长度-1的次数,而我们判断停止遍历的条件应该是,他们遍历一次不再进行交换,说明顺序已经正确,无需继续往下遍历。
代码实现
希尔排序相当于是优化版的插入排序。因为其实直接插入排序就是增量为1的希尔排序,那么什么是希尔排序呢。
首先选取一个增量,假设容量为8的一个数组,增量我取4,然后增量作为分组,分成四组,间隔也为4,比如数组数是1-8,那么1,5为一组,2,6为一组依次类推,然后对每一组进行插入排序,排序结束后,增量再减半,直到增量为1时做最后一次排序。写代码的难点在于这种分组的排序,但可以在一个循环内做到,重点就是在于你比较和交换数的时候如何不影响其他分组的值。
代码如下
希尔排序,缩小增量排序,他是对插入排序的一个优化,核心的思想就是合理的选取增量。
//克努特序列如果对于大的数据更佳,h增量采用 h=(h-1)/3的递减 而初始值采用 /* int jiangge=1 while(jiangge int[] arr={5,4,7,2,9,1,77,8,3,21,66}; (arr,0,arr.length-1); System.out.println(Arrays.toString(arr)); } //可以选定范围进行快速排序,不一定是全部,所以设定一个left和right,也方便左右扫描 private static void (int[] arr,int left,int right){ //快排三步走,1.进行一次快排 2.对左边进行快排 3.对右边进行快排。一直进行下去 // (递归)直到顺序正确,判断顺序正确的(递归)边界为左扫描和右扫描扫描到了同一个位置 if(arr==null||left>=right||arr.length int temp = arr[left];//基准数,取得基准数为最左边的数 //1.根据基准数讲小于的放左边,大于的放右 while (left //右边未找到比基准数小的,一直往前找 right--; } if (left left++; } if (left // 一个数,然后将这个数砍掉,再造堆选第二个大的数,再选第三个,一直选完为止 //将这个数砍掉在这里做的操作是最后一个结点和根结点进行交换,然后下一次遍历,不遍历最后一个结点,就相当于砍掉了。 //父结点parent=[(o-1)/2] 左子结点 c1=2i+1 右子结点 c2=2i+2 //需要用到树的性质,其实数组完全可以表示 public static void main(String[] args) { int arr[]={4,10,3,5,1,2}; //一直砍,一直减 for(int i=arr.length-1;i>=0;i--){ //1.造堆 buildHeap(arr,i+1); //2.砍 swap(arr,0,i); } System.out.println(Arrays.toString(arr)); } private static void buildHeap(int[] arr,int n){ int last_node=n-1;//求出最后一个结点 int parent_node=(last_node-1)/2; //从最后一个叶子结点的父结点进行heapify,可以做到全部进行比较 //(heapify意思是对父结点和两个或者一个子结点进行比较,若父节点部不为最大值或最小值,那么进行交换,保证 // heapify之后的父结点为最大值),把数组当作数来看,依次一层往下,根结点就是0,第二层就是下标值1,2 // ,第三层就是3,4,5,6。通过从最后一个结点的父结点往上一直heapify到根结点,必定能保证选出整个数 // 的最大值。 for(int i=parent_node;i>=0;i--){ heapify(arr,i,n); } } private static void heapify(int[] arr,int i,int n){ int c1=2*i+1;//左指结点 int c2=2*i+2;//右指结点 int max=i;//先假设父结点是最大的 if(c1arr[max]){// int temp=arr[i]; arr[i]=arr[j]; arr[j]=temp; } }静态方法区是和类一起加载的,static的问题后面会详解
继承的本质是对某一批类的抽象。比如学生,老师类都是抽象类,他们都是人类,那么人类就是对这一批类的抽象,也就是抽象的抽象。
extends的意思是扩展。子类是父类的继承。
java中只有单继承,没有多继承,所有人类都继承object(特殊)
idea小快捷键,ctrl+h查看继承树
this关键字和super关键字注意点:
super:super只能出现在子类的方法中或者构造方法中,super代表的是父类对象的引用。
this:this代表调用者这个对象,而this在没有继承的情况下也能够使用,而super只能在继承关系的子类中才可以使用。
方法重写(其实也就是多态了):重写只存在方法的重写,和属性无关,属性可以(重名)扩展,但是不是重写。
而且重写只是重写非静态的方法。
如果我创建两个类 A为子类 ,B为父类,就算B b=new A()父类的引用指向了子类,这个在static的情况下,就算new A,如果有调用和父类同名的方法,默认还是会调用B的方法。
但是如果是非静态方法 那么B b=new A()会执行A中重写了父类的方法 。
private私有修饰符修饰之后,和继承都没有什么关系了,私有的就是只有自己才可以用,儿子都不行。
一个子类被加载,它的父类必定会先被加载。
这种机制使得引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,只有在程序运行的时候才能确定到底引用变量会指向哪个类的实例对象,引用变量发出的方法调用的是哪个类的方法只有程序运行的时候才能够决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。
有点长,编辑器卡了,IO流,反射,static,多线程,代码内存详细分析,网络编程,String和hashcode,equeal的知识留到下一章节写。新人一枚,写的不好或者不对的地方,欢迎各位大佬指正。
强力推荐B站狂神说Java!!!!老师非常好,而且非常擅长拉屎。
上一篇:你必须知道的 Java 基础知识
下一篇:Java编程入门必备知识
皓盘云建最新版下载v9.0 安卓版
53.38MB |商务办公
ris云客移动销售系统最新版下载v1.1.25 安卓手机版
42.71M |商务办公
粤语翻译帮app下载v1.1.1 安卓版
60.01MB |生活服务
人生笔记app官方版下载v1.19.4 安卓版
125.88MB |系统工具
萝卜笔记app下载v1.1.6 安卓版
46.29MB |生活服务
贯联商户端app下载v6.1.8 安卓版
12.54MB |商务办公
jotmo笔记app下载v2.30.0 安卓版
50.06MB |系统工具
鑫钜出行共享汽车app下载v1.5.2
44.7M |生活服务