【自学java】 语言入门(一)学前班 基础语法

(当年0基础时,学习 java 记的笔记,还挺有趣的。许多话十分不严谨!重要内容专题重写。)

1.1 环境搭建

1.1.1 java跨平台语言

众所周知,java是跨平台语言(在不同操作系统下运行)。*面试题:为什么 java 跨平台?为什么说 java 是半解释型半编译语言? *

需要先了解一下,java 程序的 windows下的开发过程:
1.编写可阅读代码,保存为 .java 文件。
2.jvm编译.java文件,生成 .class 文件。(字节码)
3.jvm解释.class文件,生成 .exe 文件。(机器码)
这里提到的 jvm 是 java 虚拟机,包含于 jre下(java运行环境),而 jre 又包含于 jdk(java开发套件)。所以,java 程序没有跟操作系统关联,不同平台对应不同的jdk,所以 java 程序具有跨平台性。类比window下的可执行文件.exe,不具备跨平台执行能力。

经常会有面试将 java 与 c++ 对比:都是面向对象语言,但c++是编译型语言,直接将代码编译成为二进制代码(可以讲,exe文件就是二进制文件,将exe文件还原成你能看懂的代码就是逆向工程),而且C++面向对象不够“纯正”,好像不写在类里也可以执行。我个人认为java更友好一点, 虽然速度慢,但没有指针啊,不用手动垃圾回收释放内存啊,没有奇怪的功能(goto、自动强制类型转换),还具有平台无关性。

其他:
java 写一个 helloworld 要比其他语言费劲多了, 好几行!好像有一本很出名的书写道:“java语言写起来的复杂程度,证明了这种语言的设计缺陷。世界上流行的语言都会向更快的方向发展,(一些脚本语言,python ruby )…”这我还是不敢苟同的,java语言这种纯面向对象,对于工程上来说是非常严谨的,往后学就知道了。

1.1.2 环境搭建与相关名词

安装jdk :Oracle官网
环境变量的配置:略, 参考百度(win10 , 只需要配置两个环境变量了。 环境变量只是添加一些 exe 文件的寻址路径)

javac.exe -> 编译器
java.exe -> 解释器

在完成第一个程序之前,需要了解一下dos的基本操作命令:

命令 功能
cd 切换操作目录
dir 查看目录下文件
d 切换到盘符d
cls 清屏
cd.. 返回上级目录
copy 复制
move 移动
del 删除
ipconfig 查看ip
ping 链接某ip

1.1.3 HelloWorld

1、打开记事本,键入以下代码:

public class HelloWorld{
  public class void main (String[] args){
      System.out.println(“HelloWorld!”);
  }
}

2、保存为HelloWorld.java,

3、CMD中切换到该目录下,执行:
javac HelloWorld.java
java HelloWorld

1.1.4 集成开发环境IDE

主流:intellij

记事本:Notepad++ 、Sublime

操作系统:CentOS 7 、Ubuntu 18.4

注:收回我的推荐,idea是真好用,谁用谁知道

1.1.5 其他

在《java程序员面试笔试宝典》中,关于main方法面试题 :如何在main 方法前输出某语句 ?
其实, main 就是这个程序的入口 , 凡是程序总要有个入口。 public static void main 不能改动,String[] args 就是执行 java XXXX ...(参数)...后面的参数 。至于加什么其他关键字,synchronized 、final 、加 static 块都是可以的。

1.2 变量与数据类型

1.2.1 命名规则

文件命名规则:

类名首字母大写;运行类名与文件名相同。如果public修饰,必须相同。所以,一个.java文件中只能有一个public修饰的类。

变量命名规则:

首字母小写,使用驼峰命名法,望文生义。避免java内部的关键字与标识符。
例如:int a; int a=1,b,c,d;同时声明多个变量,并初始化 a。

注:这里面有一个有趣的点,声明一个变量a ,是否分配内存空间?
答:对于基本类型变量声明却没有赋值,jvm是没有分配内存空间,而是在赋值的时候分配内存空间。而引用类型变量是一个地址指向了堆内存(即分配了一个地址空间),new肯定是开辟了内存空间的。(网上也有其他答案,我比较认同这个)

1.2.2 变量

1、 变量 : 简言之就是来标识一块内存,代表内存的一块空间。不同数据类型,需求的内存空间大小不同。

例如 a = a+10; //取出a内存空间的值+10 后再放入a内存空间。

2、变量可以重新赋值,不能重复定义。

3、变量的作用域 :{} 表示作用域,变量只在最近的作用域内有效。

4、使用变量前必须初始化并且赋值。

1.2.3 计算机基础 : 进制简介

1、
十进制(0-9):正常输入即可
八进制(0-7):数字开头0
十六进制(0-9 ABCDEF):数字开头0X
二进制(0,1):数字开头0B

2、x进制,即逢x进1

3、不同进制转换,通用思路:借助十进制桥梁 (逐位*10^n;除2取余数)

4、java进制:采用二进制补码方式表示数字。(32位)
补码:想表示负数,相当于在数轴上将值域折半左移。原理:1打头二进制数字,表示负数,即第一位位符号位(注意1000表示 -8)。转换规则:逐位取反加1 。

范围:-2^(n-1) ~ 2^(n-1)-1

推理得出:int的最大值+1 ,变成了int的最小值。

1.2.4 数据类型

1、 知识储备:

A B
1byte字节 8bit 位
1kb 1024 byte
1m 1024 kb
1gb 1024m
1tb 1024gb

2、八种数据类型:

类型 空间 范围 其他
byte 1 byte -128 ~ 127 8位
short 2 byte -32768 ~ 32767 不使用
int 4 byte -2147483648 ~ 2147483647 整型
long 8 byte 太长 64位 长整型
float 4 byte 3.402823e+38 ~ 1.401298e-45 浮点
double 8 byte 1.797693e+308~ 4.9000000e-324 双精度浮点
char 2 byte 0~65535 字符(无符号)
boolean 1 byte --- 逻辑变量TF

详情见数据类型专题。

1.2.5 其他

1.java中整数**默认int **,小数默认double
2.long a = 100000000000L;//结尾加L
3.计算机中浮点数不能做精确运算,精确运算用其他方法。

1.2.6 类型转换

小范围到大范围自动完成转换,大范围到小范围强制类型转换,并丢失精度 。 例如(int)10.5

1.3 基本语法

1.3.1 运算符

1、常规运算符

\+ 加    两数 相加
\+ 连接符  字符串之间连接
- 减 相减
* 乘 乘积
/ 除 相除 整数相除 取整数部分
% 取模    整数相除取整数、浮点相除取浮点
++ 自增   区别 a++ 与++a
-- 自减   同上
< 小于    返回布尔
\> 大于   返回布尔
== 等于   返回布尔
= 赋值    右边赋值给左边
&& 与    返回布尔
|| 或    返回布尔
! 非 返回布尔
+=  a=a+5 表示为 a+=5

注意: 如果a = 10 , 表达式 a++ 值 10;表达式 ++a 值 11。不论如何,此条语句过后a都自增,a的值是 11 。

2、未介绍运算符:<=小等于、 >=大等于、!= 不等于、(条件表达式)?(真,结果1):(假,结果2);条件运算符,也叫三元运算符。

逻辑运算符注意事项:注意"短路"操作!
例如,&&的第一条件为F,则第二条件 (表达式)不执行!同理,|| 。

3 、移位运算:

>> 有符号右移 , >>>无符号右移
int a = 10; a = a>>2 ;表示 10的二进制1010右移两位,0010,a值为2 。 右移一位相当于除以2取整 。

符号问题:对于负数,>> 运算补位补1 。>>>无符号移位 : 补0。<<左移,末位均补0。

中位数写法:a > b , mid = b + (a - b)>>1;

4、很少使用的位运算符:& ^ | 位与、 异或 、位非 ,用于一些算法题的特殊场合。注意异或这是一个可逆操作。

1.3.2 条件语句

条件语句属于程序的分支结构。程序运行有三种结构:顺序结构、分支结构、循环结构。

if(条件1){
  code;
}else if(条件2){
  code;
}else{
  code;
}

注 :
1、如果代码只有一行,可以不写{}。
2、If 与三元运算符的转换问题:三元运算符,必须有结果返回。而 If 只为分支语句 , 可以没结果。也就是说,if可以控制流程,三目运算符要有返回值,所以其常在 return 使用。

分支结构中的 Switch 选择结构

switch(表达式)
{
  case 1 :
    ....;
    break;
  case 2 :
    ....;
    break;
  default:
    ...;
    break;
}

break关键字:跳出swtich 大括号;如果case中没有break,就会顺序执行下一个case。
default 最多只能出现一次,可以出现在和 case 并列的任意位置,无论如何都会执行default 代码。

面试题:
1.switch中允许的数据类型:byte,short,int,char,enum(枚举),String
2.switch的简化:case执行结果相同,语句可以省略,只写一次。

case 1: // 穿透性,没遇到break,往下执行。
case 2:
  System.out.println(“ccc”);
  break;

1.3.3 循环语句

1、for循环:for(表达式1;表达式2;表达式3){循环体;}

执行顺序:执行表达式1,判断表达式2,t就执行循环体,执行表达式3,判断表达式2...开始循环。
常用格式:for(初始化;条件;增量){循环体}

注:
continue:结束本次循环,执行下一次(跳出循环体,执行第三个表达式)
break:结束整个循环

注:
1)双重for和嵌套for,能不用就少用 ,复杂度过高
2)break 在多重循环中,只能先跳出最近的for结构。
3)如何跳出最外层for,利用了label 标签 outer:

outer:for(...){
  for(...){
    break outer;
  }
} //使用不多

4)foreach 用的很多,与迭代器一起说。

2、while循环:while(条件){循环体}

执行顺序:条件真,执行循环体;假,退出循环。

常用结构:
while(n!=0){...}
while(true){...}

注:
1)如果用 do-while 循环体, 循环体最少都要执行一次。
2)无限循环(死循环):
while(true){}
for(;true;){}

1.4 数组

1.4.1 基本概念

数组:存数据用的“容器”,一堆相同类型数据的集合

定义:数据类型[] 变量名=new 数据类型[存储大小];

数组编号:index 索引 ,也称下标

访问数组元素:数组名[索引]

例如:

int[] scores = {99,67,81} ;
System.out.println (score[0]); // 得到元素99

数组在内存中原理:
初始化时,scores 指向了一个连续堆内存空间, scores 含有这个数组的地址,我们称为“scores 引用了这个数组”。所以数组是引用数据类型不是值数据类型。而且数据开辟的是堆内存,所以需要 jvm 垃圾回收机制,来收回空间。在强调一下,数组是开辟了一块连续内存,在 java 中是属于引用数据类型,这点可能有别于其他语言 , 如 golang。

数组属性:数组的长度 = 数组名.length
数组遍历:索引方式取出所有元素,for 循环。

例如:

for(int i = 0 ; i <= scores.length ; i++){
  System.out.println(scores[i]);
}

数组很重要的几个点:
1、数组是堆内存中一块连续的内存空间。
2、因为其连续性,对数组元素的查找速度是O(1)。
3、声明变量是个引用,指向首地址。

1.4.2 数组常见异常

1.索引越界异常
2.空指针异常:不合适地把数组名存放的地址释放
虽然 java 中声称没有指针,其实在数组处理中是有指针思想的。通过函数的形参指向实参的同一个数组,所以对数组操作的方法不需要返回值,直接操作原数组。

1.4.3 数组的排序

排序三种基本方法:
1)选择排序:
从第一个元素开始,每个元素与后面的元素比较,选出最小的方法在前面。

public static void sort(int[] a) {
        /*选择排序*/
        int i,j,t;
        for ( i = 0; i < a.length; i++) {
            for ( j = i + 1; j < a.length; j++) {
                if (a[j]

2)冒泡排序:
相邻两个元素比较,最大的放在末位,每次循环少比较一个元素。

public static void sort2(int[] a) {
        /*冒泡排序*/
        int i,j,t;
        for ( i = 0; i < a.length-1; i++) {
            for ( j = 0; j < a.length-1-i; j++) {
                if (a[j] > a[j+1]) {
                    t = a[j];
                    a[j] = a[j+1];
                    a[j+1] = t;
                }
            }
        }
    }

3)插入排序:
原理是在一个排好序的数列中插入一个元素。被插入的元素从后往前,逐一与已排好序的最末位元素比较。

public static void sort3(int[] a ) {
        /*插入排序*/
        int i,j,t;
        for (i = 1;  i < a.length; i++) {
            t = a[i];
            for ( j = i-1; j >= 0 && t < a[j]; j--) {
                a[j+1] = a[j];
            }
            a[j+1] = t ;
        }
    }

注:
数组中提供的排序API: Arrays.sort(a);
其他排序方法:希尔排序,快速排序等
效率问题:冒泡排序性能比较差,自带排序算法效率最高,插入排序略优于冒泡

1.4.4 多维数组

二维数组:是由多个一维数组组成,每一个一维数组都是二维数组的元素。二维数组的长度就是其中一维数组的个数。

定义方式:

  1. 同一维数组 int[][] arr = new int[2][3];
  2. int[][] = {{1,2},{42,56,8},{10,5,11}}; 这个二维数组长度是3 。

二维数组的遍历:嵌套 for 循环
二维数组变量名依然存放地址 :遵循引用数据类型原则

注:杨辉三角

              int[][] arr = new int[8][8];
        for (int i = 0; i < arr.length; i++) {
            arr[i][0] = 1;
            if (i >= 2 ) {
                for (int j = 1; j < arr[i].length; j++) {
                    arr[i][j] = arr[i-1][j] + arr[i-1][j-1];
                }
            }
            if (i != 0) {
                arr[i][i] = 1 ;
            }
        }
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0 ; j <= i ; j++) {
                System.out.print(arr[i][j] + " ");
            }
            System.out.println();
        }

三维数组:(略)
同理,由多个二维数组组成,长度就是二维数组的个数。
例如:a[1][3][1] 第二个二维数组中的第四个一维数组的第二个元素。

1.4.5 其他

可以调用系统函数进行操作。

  1. 数组的复制:
int[] a = {1,2,3,4};
int[] b = new int[a.length];
System.arraycopy(a,0,b,0,a.length);
// 参数:源, 起始点, 目的, 拷贝起始点, 复制个数
  1. 数组的扩容:
a = Arrays.copyOf(a,2*a.length);//new了一个新内存空间

a 数组扩容两倍长度,复制给a。本质是 这个方法新开辟了新的内存空间。

  1. 数组的位置插入一个元素:a[index] = 123;

  2. String 就是多个 char 组成的一维数组

  3. Scanner s = new Scanner(System.in); s.nextInt(); 键盘读入

1.5 函数

1.5.1 综述

在 java 中, 函数也叫方法!
(在其他语言如 golang 中 函数和方法是有区别的!)

函数存在的意义:
重复利用一段代码 。 每次使用的时候替换相应变量(参数)即可,达到减少代码量重复使用的目的。

1.5.2 定义

1、 格式

修饰符 返回值类型 方法名(参数类型 参数名){
  内容;return;
}

例如:public static int XXX(int a,double b){}

2、 调用:
方法名(传递参数);

注:

  1. 方法定义参数是形参,方法调用是实参
  2. 通常方法名要小写,在 main下面并列位置
  3. 函数遇到 return 就结束了,不会执行后面的代码

1.5.3 内存空间与参数传递

1、 简述 :内存分为堆内存和栈内存,堆内存通过垃圾回收器 gc 回收空间。栈内存,先进后出自动释放空间。即方法结束,内存自动清理。

注:jvm 内存空间划分:

  1. 程序计数器 : 线程私有,cpu 切换时候找到上次执行位置
  2. 本地方法栈 : c++调用本地方法
  3. 虚拟机栈:所有方法运行时进入地方(main方法 压栈运行)
  4. 虚拟机堆:储存 容器 和 对象,实际存放对象的内存空间。
  5. 方法区:存放类信息和常量池

2 、参数传递问题:
函数定义时,接受的传递参数叫形式参数。main方法调用函数的时候,传递的参数叫做实际参数。形式参数只在函数内部起作用,即使与实参叫了同样的名字也不会改变main中的实参(只是指的是 基本数据类型, 也就是值类型 )。这个问题产生的原因是,方法进栈内存中运行,而参数变量只是在栈内存中开辟了相应的内存空间,方法调用完成后出栈,方法内部的形参和其他参数空间随机被清理。

1.5.4 函数的递归

简言之,在函数内部调用自己。举一个阶乘运算例子:

public class Test{
  public static void main(String[] args){
    System.out.println(func(10));
  }
  public static long func(int n){
    if (n==1)
      return 1;
    return func(n-1)*n;
  }
}

递归的内存空间原理:利用还是栈的先进后出原理,先压栈存入10,次函数未结束未出栈,再执行函数存入9…直到return 1,得到返回值1,最里层1空间出栈,12,12*3…直到返回10!。

1.5.5 方法的重载

java 允许在一个类中定义多个同名的方法,称为方法的重载 。

要求:变量个数不同 ,或变量类型不同(用以区分),与返回值类型无关。

重载只看方法名和参数列表

你可能感兴趣的:(【自学java】 语言入门(一)学前班 基础语法)