java基础开发

JVM java具有跨平台性。java编写的程序都运行在JVM上。而JVM运行在操作系统之上。

JRE 是java程序的运行时环境,包含JVM和运行时所需要的核心库类。

JDK 是java程序开发工具包,包含JRE和开发人员使用的工具。

如果我们想要运行一个已有的java程序,那么只需要安装JRE即可。

如果想要开发一个全新的java程序,就必须安装JDK。

JDK开发工具包含了JRE运行环境,JRE运行环境包含了JVM运行核心。

JAVA是对大小写敏感的。 类名需要与文件名相同。 JAVA是面向对象的。注释可以这么写//

main方法是程序执行的起点。是主方法。

以下(在关键字开始之前)都是我的个人感悟,走的一些弯路。

声明:本人使用的是IntelliJ IDEA 2021.3.2

个人认为编程不用背,直接敲,敲多了就会了。

这是小呆学习的首个强类型语言,有的弱类型语言的习惯会容易报错,所以如果您也学过弱类型语言,那么请你不要把弱类型的那种“随意”带到强类型语言中。

原本是做PHP代码审计的,感觉现在PHP有点跌落神坛的感觉,赶紧学java,好去搞java 代码审计。

在强类型语言中,数据类型不好好学,编程两行泪,嘎嘎溢出,嘎嘎报错。

优先级不学好,编程两行泪。

有些计算机内的语句在数学里是无法成立的,所以要养成编程思维。

附上一句话:世界上没有难的编程语言,只有不愿意学的人。

有些时候,一些东西都是JAVA底层写的,所以初学阶段没必要去纠结,(正在被PHP底层折磨的daidai.jpg)有些东西,到了后期自己可以看底层原理,再去二次理解,会有新的彻悟。前期,你不用去深究,等到后期JAVA练熟了,C也学的很牛逼了,能开发jsp了,或者说能写软件了。你就可以尝试着去看看底层,会对这门语言有更深层次的理解,这些都是我学PHP后的感悟。

搞安全不搞编程,就和脚本小子就无异了。

编程需要发散性思维。

别看有些东西好像很枯燥,其实面试官比较喜欢问这些问题,例如NULL和undefined的区别。(描述的话文字较多。)例如String是基本的数据类型吗?(String是引用类型)说实话以前我也觉得程序能跑不就行了,现在想想还是觉得这些东西真的重要。

不同语言有不用语言的特性,不要用另外一个语言的特性来测试这个语言(我就因为之前学的是PHP,PHP也有面向对象,但是不是纯面向对象的,有些时候写java就会不习惯)。

而作为信息安全的或者简称搞安全的,对于安全专业的人来说,语言的特性还是非常重要的。

最近的几个大型漏洞,都是JAVA下的一些项目的,包括Log4j等,赶紧学JAVA

0X01关键字

关键字指的是含有特殊含义的,被保留的。不能随意使用的字符。

关键字的特点:

​ 1.完全小写的字母。类似于public,像Public就不是关键字,因为java对大小写敏感。

​ 2.在编辑器中有特殊颜色。

0x02标识符

类的名称,变量的名称,方法的名称,这些自己起的名字都叫做标识符。

标识符的硬性要求有

1.标识符不能以数字开头。

2.标识符不能是关键字。

3.标识符可以包含英文字母26个,0-9数字、$和_

命名规范

1.类名规范:首字母大写,后面每个单词首字母大写。(大驼峰式)

2.类变量规范:首字母小写,后面每个单词首字母大写。(小驼峰式)

方法名规范同变量名

0x03常量

常量是在程序运行期间,固定不会改变的量。

常量的分类:

1.字符串常量:用双引号起来的部分,叫做字符串常量,。

2.整型常量

3.浮点数常量

4.字符常量:与字符串常量不同的是,他是用单引号引起来的单个字符,就做字符常量,例如'A'

5.布尔常量

6.空常量:NULL。

空常量不能直接被打印输出。

0X04数据类型

数据类型分为基本数据类型(整数型,浮点型,字符型,布尔型),引用数据类型(字符串,数组,类,接口,Lambda)。

四类八种

整数型关键字:byte short int long

浮点型关键字:float double

字符型:char

布尔型:boolean

强类型语言的内存占用还是要记一下的。

java中的默认类型 整数类型是int,浮点类型是double.

注意事项:

1.字符串不是基本类型,而是引用类型。

2.浮点型可能只是一个近似值,而不是精确的值。

3.浮点数当中默认类型是double,如果一定要使用float类型,需要加上一个后缀F。

​ 如果是整数,默认为int型,如果要转换为long类型,需要加上一个后缀L。

例如:System.out.println(100L);

0X05类型转换

1.自动类型转换

如果写一个System.out.println(1024);//默认是int类型

System.out.println(3.14)//默认是double类型。

long类型去定义变量,变量后面必须加L

long num1=100;如果是这么写的,就会将右侧的int类型,交给左侧的long类型进行存储。

int-->long 符合了数据范围从小到大的要求。

这一行代码就会发生自动类型转换,

double num2 = 2.5F;

2.5加个F就变成了float类型,如果这么些就会将float交给double进行处理。

float->double,符合了数据范围从小到大的要求。

2.强制类型转换

当我们将类型进行转换,但不符合从小到大规则,就需要强制类型转换。

例如当long转换为int的时候,不是从小到大。不能发生自动类型转换。

int num = 100L

如果这样的变量直接输出就会报错,这时候就需要强制类型转换

int num=(int)100L//可以这样进行转换。

强制类型转换不推荐使用,有可能会发生精度损失,数据溢出。

例如int num=6000000000;

因为int最后多只能存储到21个亿。我们写了60亿,就会报错。

这时候我们有两个选择。

1.将int类型重新声明为long类型

2.这么写 int num=(int)6000000000L,那么就要发出疑问了,为什么你后面都加L了,都变成了long类型了,那为啥还需要前面加个(int)进行强制类型转换呢,。

long-->int 从大到小,并不能进行自动类型转换,会报错,所以还是进行强制类型转换,但是!这样就会导致数据溢出。

什么是数据溢出,可以抽象的理解为当水的体积过大,碗装不下,那么就会导致水的溢出,那么数据溢出就是当数据过大这个类型装不下就会导致数据溢出。

当double转换为int的时候,进行强制类型转换

int num=(int)3.5;

那么,这个.5就会消失不见。这就是强制类型转换导致的精度丢失。程序不存在四舍五入,丢了就是丢了。

byte/short/char这三种类型在进行运算的时候,都会被首先提升为Int类型,然后计算。

当我们声明一个char类型的变量。然后对他进行+1

char zifu='A';

System.out.println(zifu+1);//当大写字母A+1,那么就会输出66.

这是因为当char类型加上int类型时,他会将char类型的字符串转换成ASCII,然后+1.

我们对照ASCII表。可以知道大写字母A的ascii值为65

我们写一段代码。

注意我前面加粗的语句。

byte在进行运算的时候首先会被提升为Int类型,所以这样写,就会报错

可以看到直接划上了红色波浪号,代表有错误。

是因为byte+byte首先会变成int+int,int+int当然是等于int.我们前面讲到过,当大的类型转换到小的类型的时候要进行强制类型转换,这边sum2+sum1的int结果,直接交给byte,因为byte比int小,所以无法进行自动类型转换,故这里会报错。有两种解决方式

1.将result定义为int类型。

2.将得到的结果强制类型转换为byte型。

当然可以选择前者,肯定选择前者。尽量不用强制类型转换。如果sum1和sum2的值过大的话,即使是强制类型转换也会报错。

当short和byte进行相加的时候,还是前面加粗的那句话,会先转换为int,然后进行运算。

因为数据在short的范围内,也可以使用强制类型转换。

当char字符型转换为int型的时候,其实输出的就是对应字符的ascii值。

因为数字和字符的对照关系表有两张

1.就是ASCII码表(美国信息交换标准代码);里面包含了[A-Za-z0-9]还有一些特殊字符

2.UNICODE表:万国码。和ASCII一样都是对照关系,开头0-127部分和ASCII一样,但是从128开始就包含有更多的字符。包括像简体中文,繁体中文,韩文等文字都能包含进来。包括我们使用的emoji表情,也在unicode码表中。unicode的使用范围是很广的。

那么当我们声明一个汉字,输出出来的结果就是对照unicode

可以看到呆对照21574

我们总结一下

1.强制类型转换一般不推荐使用,因为有可能会发生精度损失,数据溢出。

2.byte/short/char可以进行数学运算

3.byte/short/char这三种类型在进行数据运算的时候,都会被提升为int型,然后计算。

4.boolean不能发生数据类型转换。

0X06.运算符

1.算数运算符

首先算数运算符:+ - * / 取模%,一共五种。

表达式:a+b,a/b,a%b,也是懂得都懂。

还是要讲讲除法

对于一个整数的表达式来说,除法用的是整除,结果仍然是整数(再重申一遍,不会进行四舍五入)如果要得到余数,使用取模运算。

一旦运算符中有不同类型的数据,那么结果将是数据类型范围大的那个数据类型的结果。

例如double+int那么就等于double

2.加号的用法

1.对于数值来说,那就是加法。

2.对于char类型来说,在计算之前,char会提升为int,然后计算。

3.对于String(String并不是关键字,而是个类),加号代表字符串连接操作。

当我们将String+int的时候,得到的结果为String.

当数据类型和字符串进行连接的时候,都会变成字符串。

当String+int+int的时候结果是3个值拼接的结果。

这就是优先级问题

他与小学教的一样,乘除优先级高于加减,括号内的先计算等。

3.自增自减运算符

首先,我看到很多人,总是搞不明白--a和a--的先减后减的一个问题,其实以前我也分不清楚。

首先自增:++ 自减--(每次+1或者-1)

使用方式:

1.单独使用:不和其他任何操作混合,自己独立成为一个步骤

2.混合使用:和其他操作混合,例如与赋值混合,或者与打印操作混合等。

使用区别

  1. 再单独使用的时候,++num和num++并没有区别。

这边写一个程序来证明观点

2.在混合使用的时候区别就大了。这也是很多人分不清前++和后++的区别

前++是,变量立刻+1,然后拿着+1后的结果拿去使用(先加后用)

后++是,首先使用变量原本的数值,然后再让变量+1(先用后加)

例如在System.out.println的输出语句中使用

4.赋值运算符

赋值运算符分为基本赋值运算符和复合赋值运算符。

基本赋值运算符:就是一个等号,代表将右侧的数据,交给左边的变量。

复合赋值运算符包括+= -= %= /= *=

a+=3 相当于 a=a+3

a-=3 相当于a=a-3

a%=3相当于a=a%3

a/=3相当于a=a/3

a*=3相当于a=a*3

那我们就运用到实际的编程当中

那么我们这边就要讲讲复合赋值运算符其中隐含了一个强制类型转换。

平时我们看看,觉得哎,很平常,很容易可以看出结果为15。,

这边我们知道这个5是int类型,那么就会进行一次强制类型转换。

那么又要到一个注意事项阶段了。

1.只有变量才能使用赋值运算符,常量不能赋值。

2.复合赋值运算符其中隐含了一个强制类型转换

5.比较运算符

首先比较运算符有== ,<,>,<=,>=,!= 6种。

当两个数据之间进行比较的运算,运算结果都是布尔型 true 或者false。

6.逻辑运算符

逻辑运算符真是编程入门的必学

与(并且)或(或者)非(取反),&& || !

与:全部都为true,即为true

或:一个为true,就会true

非:flase,即为true,ture即为false。

实验时间。

可以看到一,一对应

0X07三元运算符

首先来讲一下

一元运算符:所谓一元运算符就是只要有一个数据就能操作的运算符,例如:取反!,自增,自减等。

二元运算符:需要两个数据进行操作的运算符。例如:+,-等

三元运算符:需要三个数据才能操作的运算符。

格式: 数据类型 变量名称=条件判断?表达式A:表达式B

流程

1.首先判断条件是否成立

​ 如果成立就将表达式A的值赋值给变量,如果不成立就将表达式B的值赋值给变量。

实验阶段

简单的写一个判断最大值的程序

如果a大于b就会将a的值赋值给max,否则就将b的值赋值给max。

来讲讲注意事项

1.必须同时保证表达式A和表达式B的值都符合左侧数据类型的要求。

例如 int result = 3 > 4 ? 2.5:10;就是错误写法。

他不会去管你存入的是谁,只要表达式中有不符合数据类型要求的值就会报错。

2.三元运算符的结果必须被使用。

0X08方法

重点来了,方法是面向对象的关键。

方法的概念

我们可以把我们自己定义成方法。那么我们的动作就是吃。

那么当我们调用到me的时候他就会输出吃。

这时候就要提一下关键点了。

方法体:也就是方法{}花括号的内容,可以包含任意条语句。

注意事项

1.方法需要调用,方法不被调用的时候是不会执行的,一般我们把一些方法放在一起的时候,称之为封装。方法需要调用才能被执行。

2.方法的定义不能产生嵌套包含关系,也就是说不能方法里面在嵌套一个方法。

方法可以这样调用

方法名称();//没错就是这么调用。java和PHP不同,java调用方法,要将方法的调用放到main主方法中。

那么我们实验一下

可以看到我们调用到了me方法中的输出语句,输出了吃。

0x09 Jshell脚本工具

首先讲Jshell的启动。

如果环境变量没问题的话,一般直接在CMD中输入jshell就能启用他。

JDK9以上才能用Jshell

一般在IDEA中可以这么调用

推出Jshell,可以使用/exit

在CMD中一般这么使用

我本人不咋使用,所以就不过多讲解了。一般都是直接写代码,然后编译运行。

一般用Jshell的情况是,一些简单的代码,只想运行一下就好的,一般会在Jshell中运行。

0X10编译器的优化

首先我们来看一段代码

大家都知道输出的是30,

但是30明明是一个int型,(可能会有人说你都声明一个byte型了,怎么会是int型呢)

这里就要讲到编译器的优化了。

对于short/byte/char三种类型来讲,如果右侧的赋值没有左侧数据类型的范围,那么就会自动隐含的帮我们进行强制类型转换。或者说自动帮我补上(byte)(short)(char)
这也是面试官比较喜欢的问题。

所以可以总结一下

1.如果没有超过左侧的范围,那么编译器会帮我补上强转。

2.如果超过了左侧的范围,那么编译器会报错。

我们之前知道,当byte short char 进行运算的时候会先提升成为int,故会变成Int+int,结果当然为int.故这种写法之前就讲过是错误的。

但是我们这边在做一个实验,当我们不用变量用常量的使用。例如short=10+5;

我们都知道这个10是一个int,5也是一个int。按照我们的常识int+int不能直接等于short会报错,但是这边我们却能直接执行。

可以看到直接返回了13.

这是因为当我们再给变量赋值的时候,如果右侧的表达式当中全是常量,没有任何变量,那么编译器javac将会直接将若干个常量表达式计算得到结果。编译之后,得到的.class字节码文件当中相当于short result=13;

右侧的常量结果(是结果奥)数值,没有超过左侧范围,所以正确。

一旦表达式中有变量参与,那么就不能这么写了。

0X11流程控制与顺序结构

1.顺序结构

顺学结构是最简单的。谁在前面谁就先执行,谁在后面谁就后执行。

开始->步骤1->步骤2->步骤3->步骤4->结束

2.选择结构

​ 一.if语句

if语句,老生常谈了一共有三种格式

单if语句 if(关系表达式){}

执行流程

1.首先判断关系表达式看结果是true还是false

2.如果是true就执行语句体。

3.如果是false就不执行语句体,直接结束。

if...else语句

执行流程

1.首先判断关系表达式结果为true,还是false。

2.如果为true,就执行第一个语句体。

3.如果为false就执行false语句体。

4.结束.

具体的使用情况

判断奇数还是偶数

3.if...else if...elseif...else

else if可以写很多次。

这个语句相比于前两个会稍微复杂一点。

他的流程图是这样的。

1.当条件判断1为true的之后直接执行语句体1。

2.当条件判断1为false的之后去进行判断条件判断2

3.当条件判断2为true的之后,就会去执行语句体2

4.当条件判断2为false的之后,就会去执行else

那么我们可以写一个实验。

0X12选择结构(switch语句)

1.switch语句的格式是

switch(表达式){

case 常量值1:

语句体;

break;

case 常量值2:

语句体;

break;

case 常量值3:

语句体;

break;

default:

语句体;

break;

}

他的流程就是:

1.首先计算出表达式的值。

2.与各个case依次比较。一旦有对应的值,就会执行对应的语句体,。

3.如果没有case匹配,那就执行default:

break是结束关键字。

实验

注意事项:

1.case 后面的数值不能重复。

2.switch小括号中的值只能是以下的集中数据类型:byte/short/char/int,从JDK1.7开始,支持了String引用类型可以往小括号内写了。

break语句一般情况下不能省略。

因为如果没有break,匹配到哪一个case,就会一直向下执行,直到遇到break或者整体结束。

0X12循环语句

while循环,一般包括for循环,while循环,do...while,

循环结构的基本组成部分,一般可以分为四个部分

1.初始化语句:在循环开始最初执行,而且只做唯一一次。

2.条件判断:如果成立,则循环继续,如果不成立,则循环退出。

3.循环体:重复要做的事情内容,若干行语句。

4.步进语句:每次循环之后都要进行的扫尾工作,每次循环结束之后都要执行一次。

1.for循环

for循环语句的基本格式:

for(初始化表达式;布尔表达式;步进表达式){

​ 循环体

}

1.执行初始化语句

2.执行布尔判断,步进表达式

3.如果被人判断为true就继续执行,如果为false则停止执行。

基础算法:

99乘法表。

具体代码

public class Test{
    public static void main(String[] args) {
        int result=0;
        for(int i=1;i<=9;i++){
            for(int y=1;y<=i;y++){
                result=i*y;
                System.out.print(i+"*"+y+"="+result);
                System.out.print(" ");
            }
            System.out.println("\n");
        }
    }
}

2.while循环

1.进行条件判断

2.如果为true继续执行,如果为false则结束。

具体实例

public class Test{
    public static void main(String[] args) {
        int i=0;
        while(i<=10){
            System.out.println("123");
            i++;
        }
    }
}

3.do..while

do..while其实和while循环没多大区别。

do..while和while的区别在于他将条件判断放在了后面,先执行do里面的循环体,然后执行步进语句,然后在执行判断语句。

具体实例

public class Test{
    public static void main(String[] args) {
        int i=0;
        do {
            System.out.println("123");
            i++;
        }while(i<=10);
    }
}

做一道练习,分别用三个循环来求出100以内的偶数和。

for循环。

public class Test{
    public static void main(String[] args) {
        int result=0;
        for(int i=1;i<=100;i++){
            if(i%2==0){
                result+=i;

            }
        }
        System.out.println(result);
    }
}

while循环

public class Test{
    public static void main(String[] args) {
        int i=0;
        int result=0;
        while(i<=100){
            if(i%2==0){
                result+=i;
            }
            i++;
        }
        System.out.println(result);
    }
}

do...while

public class Test{
    public static void main(String[] args) {
        int i=0,result=0;
        do{
            if(i%2==0){
                result+=i;
            }
            i++;
        }while(i<=100);
        System.out.println(result);
    }
}

至此循环练习就结束了。

break和continue都是结束循环语句,但是不一样,break是结束整个循环,continue是结束此次循环,使其直接进入下次循环,

0X13方法

方法就像一个工厂,当你调用这个工厂,将参数送入工厂,再出来的就是返回值。

定义方法的格式

我们知道主方法是这么定义的

public static void main(String args[]){}

定义方法的完整格式就是

修饰符 返回值类型 方法名称(参数类型 参数名称,....){

方法体

return 返回值;

}

修饰符:现阶段的固定写法,public static

返回值类型:也就是方法最终产生的数据结果是什么类型

多个参数可以用逗号分隔。

return的两个作用。1.停止当前方法,2,将后面的结果数据返回给调用处。

retrun后面的返回值,必须和方法名称前面的返回值类型,保持对应。

做一个练习,写一个两个int相加的方法。

首先是修饰符:piublic static

返回值类型 :int

方法名称:sum

参数列表 int a,int b;

那么就开写吧。

至此sum方法就写好了。

0X14方法的调用

1.单独调用

只要写上 方法名称(参数);

2.打印调用

System.out.println(sum(1,2));

3.赋值调用

int result=sum(1,2);

方法可以用两种方式编写。

分为有参数和无参数,

有参数需要传递参数进行编写。

无参数就不需要传递参数,。

上面的写法是有参数的。

然后可以写一个无参数的。

写个小作业

写一个方法,用于判断两个数是否相同。

我写的代码

作业2

定义一个方法,求出1-100之间所有数字的和。

就是拉莫简单。

使用方法的注意事项:

1.方法应该被定义在类中,但是不能在方法中再次调用方法,不能嵌套。

2.方法定义前后的先后顺序无所谓。

3.方法定义之后不会执行,如果希望执行,一定要调用,单独调用、打印调用,赋值调用

4.如果方法有返回值,那么必须写上“return 返回值”,不能没有

5.return后面的返回值数据,必须和方法的返回值数据,对应起来。

6.对于一个void没有返回值的方法,不能写return后面的返回值,只能能return自己。

7.对于void方法当中最后一行的return可以省略不写哦

8.一个方法当中可以有多个return语句,但是必须保证同时只有一个会被执行到,两个return不能连写。

0X15方法重载

方法重载:多个方法的名称一样,但是参数列表不相同。

看实验。通过参数列表的不同,调用不同的方法。

方法重载与下列因素相关:

1.参数个数不同

2.参数类型不同

3.参数的多类型顺序不同。

方法重载与下列因素无关

1.与参数的名称无关

2.与参数的返回类型无关。

那就做一道题把。

判断两个值是否相等。(要求不同的数据类型)

练习2

判断哪些方法是重载关系。

0X16数组的静态初始化和动态初始化

静态初始化 指定内容

动态初始化 指定长度

1.动态初始化数组的格式

数组类型[] 数组名称 = new 数组类型[数组长度];

那么我们编写一下创建一个数组,能存放10个double类型的数据

2.静态初始化数组的格式

数组类型[] 数组名称 =new 数据类型[] {元素1,元素2,...}

那么创建一个数组,里面装的全是int数字。

静态初始化还有省略格式。

省略格式为

数据类型[] 数组名称={元素1,元素2,....}

静态初始化的标准格式可以拆分为两个步骤。

动态也可以。

注意静态初始化的省略格式,不能拆分为两个步骤。

0X17访问数组元素进行获取

如果直接打印数组名称,得到的是数组名称对应的哈希值。

要获取单个数组对应的值,我们可以这样做(注意,数组的索引是从0开始算的)

那么我们就可以将索引的值赋值给一个变量。

使用动态初始化数组的时候,其中的元素会自动拥有一个默认值。

如果是整数类型,默认为0

如果是浮点型,那么默认为0.0

如果是字符类型,那么默认为'u0000'

如果是布尔类型,那么默认为false;

如果为引用类型,那么默认是null

我们可以给动态初始化数组赋值。

可以看到0号元素默认为0,1号元素为我们复制给他的123,2号元素默认为0

静态初始化其实也有默认值的过程,只不过系统自动马上将默认值替换成了大括号当中具体的数值。

0X18 java的内存划分

Java的内存需要划分为五个部分。

1.栈(Stack):存放的都是方法中的局部变量,方法的运行一定要在栈当中。

局部变量:方法的参数,或者是方法{}内部的变量

作用域:一旦超出作用域,立刻从栈内存中消失。

2.堆内存(HEeap):凡是new出来的东西,都在堆当中。

数组就在堆内存当中。

堆内存里面的东西都有地址值:16进制。

堆内存里面的数据,都有默认值。(上边有讲过)

3.方法区(Method Area): 存储.class相关信息,包含方法的信息。

4.本地方法栈(Native Mthod Stack) :与操作系统相关

5.寄存器(pc Register):与CPU相关。

0X19一个数组的内存图

还不明白可以再去看一遍

https://www.bilibili.com/video/BV1d7411B7ju?p=85&spm_id_from=333.880.my_history.page.click

https://www.bilibili.com/video/BV1d7411B7ju?p=86&spm_id_from=pageDriver

新的数组地址值就不同

0X20两个引用指向同一个数组的内存图

当我们将arrayA的地址值赋值给arrayB那么就将两个引用指向到了同一个数组

我们来看他的内存图

首先输出一下这段语句,可以发现是将arrayA的地址值赋值给arrayB

结果为
[I@1b6d3586
0
0
0
[I@1b6d3586
30
20
0
[I@1b6d3586
300
200
0

可以发现地址是没有发生变化的。

画一下流程图

有点乱哈。

可以看https://www.bilibili.com/video/BV1d7411B7ju?p=87&spm_id_from=pageDriver

0X21数组索引越界异常

如果访问数组元素的时候,索引编号不存在,那么就会发生数组索引越界异常

报错为:ArrayIndexOutOfBoundsException

原因就是索引写错了,修正索引。

0X22空指针异常

数组必须进行new初始化才能使用其中的元素,如果只是赋值了一个null值,没有进行new创建,那么将会发生空指针异常:NullPointerException

原因:忘了new

解决:补上new

0X23获取数组的长度。

可以通过数组名称.length这个格式直接获取到一个int值,为数组的长度。

数组一旦创建,程序运行期间,长度无法发生改变。

在这里有个实例(不是说,程序运行期间,数组长度无法发生改变)

那么这里是什么情况

看似是一个变量,但其实是两个数组。

变了的是新数组的地址值,而数组是没有变。其实是通过更换了数组的地址值,导致指向了另外一个新数组。

0X24遍历数组。

数组遍历我们可以通过for循环来进行。

这样不够灵活,我们可以再灵活一点。

通过length去获取长度,然后循环。

做一道练习题,求出数组当中的最大值。

新建一个变量作为存储值,然后新建一个max值进行比较,取最大值,然后输出

练习题2,反转数组。

将原本数组的[1,2,3,4]反转成为[4,3,2,1]。

0X25数组作为方法传递

写方法的三要素:

返回值类型:只是进行打印而已,不需要进行计算。也没有结果。用void(当方法定义时用void修饰时,表示没有返回值

方法名称:arrayA

参数列表:必须给我数组,所以我们参数列表定义int[] array

实现

数组做为方法的参数

当调用方法的时候,向方法的小括号进行传参,传递进去的其实是地址值。

0X26数组作为方法返回值

一个方法,可以有0,1多个参数,但是不能有多个返回值,

如果希望一个方法当中产生了多个结果数据进行返回,那么就用数组作为返回值。

首先是计算sum(总和)和avg(平均值)的分别结果,新建一个数组,将array[0]指向总和,array[1]指向平均值,然后返回array。

那么我们方法的返回值当然就是int[]了。然后调用。

数组作为方法的参数,传递进去的其实也是数组的地址值。

数组作为方法的返回值,返回的其实也是数组的地址值。

0X27面向对象思想的概述(重点)

HR:面向对象编程用英文缩写如何表达。回答 OOP

面向过程:当需要实现一个功能的时候,每一个步骤都必须亲历亲为,详细处理每一个细节。

面向对象:当需要实现一个功能的时候,不关心具体的步骤,而是找一个已有该功能的方法,来调用。

所谓的如果没有对象就new一个对象的梗就是来自面向对象

来通过一个小实验,来体验一下面向对象和面向过程

首先是面向过程

然后是面向对象

//找一个JDK给我们提供好的Arrays类,

//其中有一个toString方法,直接就能把数组编程想要的格式的字符串。

0X28从生活中的发现面向对象和面向过程(举例)

洗衣服

面向过程:找一个盆->放点洗衣液->加点水->揉->冲掉泡沫->拧干->晾干

面向对象:打开全自动洗衣机->扔衣服->按一下->晾起来

面向过程:强调步骤

面向对象:强调对象。这里的对象就是洗衣机。

面向对象思想让我们从执行者变成了了指挥者

面向对象的语言中,包含了三大基本特征,即封装,继承和多态。

0X29类与对象的关系

1.什么是类

类是指一组相关属性和行为的集合,可以看成是一类事物的模板,使用事物的属性特征和行为特征描述该事物。

现实中描述一类事物:

属性: 就是该事物的状态信息

行为: 就是该事物能做什么。

举例:小猫。

属性:名字、体重、年龄、颜色。

行为:走、跑、叫。

2.什么是对象

类是对一类事物的描述,是抽象的。

对象是对一类事物的实例,是具体的

类是对象的模板,对象是类的实体。

例如手机的设计图是抽象的,真实的手机是具体的。

所以需要抽象化的类。来实例化一个类。

java中用class定义一个类,

成员变量:对应事物的属性

成员方法:对应事物的行为。

那么我们来定义一个stduent学生类。(这边用test类替代。)

首先要想好,

成员变量,也就是学生类的属性例如:姓名 年龄

行为:吃饭 睡觉 学习

普通的方法是带有static关键字的。

成员方法没有static关键字。

那么我们就开写吧。

先写成员变量

再写成员方法。

注意事项:

1.成员变量是直接定义在类当中的,在方法外边。

2.成员方法不要写static关键字。

类不能直接使用,需要通过创建一个对象,才能使用。需要三步走

1.导包:指出需要使用的类,在什么位置

格式 import 包名称.类名称;

对于和当前类属于同一个包的情况,可以省略导包语句不写。

2.创建

类名称 对象名=new 类名称();

Test stu=new Test();//根据test类创建一个stu对象

3.使用

分为两种情况:

使用成员变量:对象名.成员变量名

使用成员方法:对象名.成员方法名(参数)

练习 创建一个手机类,并且调用

首先需要看手机有哪些属性,有哪些方法。

属性就是有哪些状态信息,例如(新的旧的,颜色,等信息。)

手机能做什么事儿。

调用手机类

在调用的时候对局部变量进行重新赋值(覆盖原本的值)

0X30 一个对象的内存图

一共有五块,但是我们现在只需要用到三块,这三块分别是堆(Heap),栈( stack),方法区(Method area)

在运行main方法之前,方法区就要最先有数据。 方法区保存的是.class相关数据。包括成员变量,成员方法。

因为先来运行main方法。要运行main方法就得进栈,所以main进入了栈空间(带着main方法内的成员进入了栈),这个过程叫做进栈也叫做压栈(为什么叫做压栈,是因为后进栈的会把先进栈的压在下面)。

new了的东西全都在堆,用new创建了一个对象。故将成员变量拿过来。放入堆中,并且给他们赋值上一些默认值。<font color=red>堆内的成员方法保存的其实是地址值</font>,地址值会保存在堆当中,成员变量是参考着方法区中的成员变量在堆当中创建,这些变量的默认值其实就是在堆里面,但堆当中的成员方法其实是一个地址值,他指向了方法区中的成员方法。

当我们将后面的new赋值给前面的变量,其实就是将堆当中成员变量和成员方法的地址值赋值给变量,根据地址我们就能找到对象。

当我们覆盖掉原本的成员变量,重新赋值的时候,首先是变量指向到了堆当中的成员变量,覆盖掉原本的值,变成我们赋予给他的值,

方法的调用。根据我们赋值(就是用来new 的哪个变量)的那个变量,首先根据栈内的地址值,找到了堆中的成员方法,通过堆中的成员方法于成员变量的地址值,找到了方法区中的成员方法。成员方法要运行也是要进栈。(只有当我们调用了方法才会进栈)

成员方法内的变量是根据方法调用得来的。当成员方法完了之后,立刻马上从栈内存消失叫做弹栈或者可以叫做出栈。当main方法也执行完成之后,也是要出栈的。当main方法也出栈了,那么整个程序的内存都会消失不见,那么程序就会停下来。

写一段代码,用来画内存图。

这是课上给的图,画图太难用了。

0X31两个对象使用同一个方法

当两个对象的时候,堆当中的成员变量和成员方法的地址值会发生变化用于给栈内的变量指向。

0X32两个引用指向同一对象的内存图
首先我们可以看到上面是Phone two=new Phone(),是重新新建了一个对象,那么当Phone two=one的时候,将one当中保存的对象地址值赋值给two 两个引用就会指向同一对象。

0X32使用对象类型作为方法的参数

例如定义一个piblic static void method(Phone param),注意这里是对象类型作为方法的参数。

<font color=red>当一个对象作为参数,传递到方法当中时,实际上传递过去的是对象的地址值。</font>

0X33使用对象类型作为方法的返回值。

首先是phone作为引用类型。用作返回值。所以public static Phone getPhone(){}

然后再main主方法内我们定义的变量要用Phone整个引用类型来进行赋值,故变成了Phone two=方法名();

代码就变成了

谁调用getPhone,就将one的地址值交给谁,这里明显是two调用了.所以就会将one地址交给two。

当使用一个对象类型作为方法的返回值,其实就是对象的地址值。

0X34成员变量和局部变量的区别

1.定义的位置不一样。【重点】

局部变量:在方法的内部。

成员变量:在方法的外部,直接写在类当中。

2.作用范围不一样[重点]

局部变量:只有方法当中才可以使用,出了方法就不能再用了

成员变量:整个类全都可以通用。

3.默认值不一样

局部变量:没有默认值,如果要想使用,必须手动赋值。

成员变量:如果没有赋值,会有默认值,规则和数组一样。

4.内存的位置不一样

局部变量:位于栈内存

成员变量:位于堆内存

5.生命周期不一样

局部变量:随着方法进栈而诞生,随着方法出栈而消失。

成员变量:随着对象创建而诞生,随着对象被垃圾回收而消失

我们前面说过局部变量必须手动赋值,否则就会报错。,但是方法中的参数也是局部变量为何不会报错呢,例如

就不会报错。

<font color=red>因为参数在方法调用的时候,必然会被赋值。</font>

0X35面向对象三大特征之封装性

面向对象有三大特征 封装,继承,多态。

封装性在Java中的体现:

1.方法就是一种封装

2.关键字private也是一种封装

什么叫做封装性,我们将一些操作封装成一个方法,然后只要在main方法里调用就好了。

看例子

当我们将一个方法写完之后,我们只需要去调用它实现我们的目的。

private就是私有化的关键字。

当我们定义某个成员方法,无法阻止不合理的数值被设置进来

解决方案:使用private关键字将需要保护的成员变量进行修饰。

一旦使用private进行修饰,那么本类当中仍然可以随意访问。但是!只要超出了本来范围之外,不能再直接访问了。不能直接访问,那我们就间接访问

好了我们开始实验。

首先还是创建一个类,我们就用Person来当作一个类吧。里面有一个show方法。

这里把姓名写成了年龄,sorry。

一个人的年纪不可能-1岁对吧。(我们调用Person类,将原本的age和name覆盖掉,定义成我们想要定义的),这时候我们发现,即使岁数-20,也能够成功。

我们知道面向对象具有封装性,所以这样写肯定是错误的。

那么我们就要将age设置成private,设置成私有的一个变量,那么我们调用的Test类内的age就会红了,就会报错了。

那么我们说过,无法直接访问,那我们就间接的访问,我们无法在类下直接去约束age的大小,那么我们就创建一个方法,将age约束,并给他赋值。

那么Person就变成了这样。我们在Test类调用Person方法的时候,我们通过Setage就能间接给age赋值,然后通过show进行读取age,并输出。

当我们将age设置为-20的时候会显示error。(但是这里出现了一个bug)你能找出来吗?

当我们执行错误结果的时候,正确的结果应该是error,而不是继续执行。

因为我们的规范,在Setage的时候,也需要一个Gatage;

我们返回值为int,就要写成public int Getage(){},最终的Person类就变成了这样。

我们这里讲一个特殊的情况,那就是Private boolean,对于私有的布尔型数据,我们需要将方法名写为isxxxx

例如我们有一个学生类。性别male是布尔类型,那么方法名就要写成isMale。(这里的age就不做约束了,我懒)

现在我们就要调用了。

用setxxx去设置private的值,用getxxx去调用。

0X36_this关键字的使用

当方法的局部变量和类的成员变量重名的时候,根据“就近原则”,优先使用局部变量。

<font color="red">如果需要访问类当中的成员变量</font>,需要使用this.成员变量。
看例子。


0X37构造方法

当我们用new创建对象的时候,就是在调用构造方法。

构造方法的名字要去类一样。

关于构造方法的注意事项

1.构造方法的名称和所在的类名称完全一样。大小写也要一样

2.构造方法不要写返回类型,连void都不写。

3.构造方法的调用是通过new关键字来调用。

4.构造方法不能return一个具体的返回值。

5.如果没有编写构造犯法,那么编译器就会默认建立一个构造方法。只是没有参数,方法体也不去做任何的事情。

6.一旦编写了至少一个构造方法,那么就会将原本的构造方法重写。

7.构造方法是可以重载的。故出现了有参构造和无参构造。

当我们new一个类的时候,构造方法会自动运行。

当我们new 一个类,实例化一个对象,那么格式就是 Student stu=new student();

0X38定义一个标准的类。

一个标准的类通常需要有四个部分组成

1.所有的成员变量都要用private关键字修饰。

2.为每一个成员变量编写一对儿Geter/Seter方法。

3.编写一个无参数的构造方法。

4.编写一个有参的构造方法。

这样标准的类也叫做Java Bean

案例,里面包括了seter,geter,无参,有参构造。

0x39_API概述和使用步骤。

API就是应用程序编程接口,JAVA API是一本程序员的字典,是JDK中提供给我们使用的类的说明文档,这些类将底层的代码实现封装了起来。我们不需要关心这些类是如何实现的。只需要学习这些类如何使用即可。要调用可以看文档。

0X40匿名对象

匿名对象只有右边的对象,没有左边的名字和赋值运算符。所以我们可以这么这么写匿名对象

new Person()

如果我们要去给局部变量赋值,我们就可以这么写。

new Person().name="daidai";

调用方法

new Person().showName();

<font color=red>匿名对象只能使用一次。下次再次使用,不得不创建一个新对象</font>

<font color="red">如果确定一个对象只需要使用一次,那就可以用匿名对象</font>

当匿名对象作为参数和返回值。

可以看这个例子,

我们使用匿名对象的方式。配合Scanner。

今天推荐歌曲
《靠近》
这首歌让我想起来了初中的时候看爱情公寓,无忧无虑的时光。
赶紧溜。快到门禁了。再见!

有错误明天再改。