基础编程
基本内容
-
注释、控制台互动、导包:
-
3 种注释:
// 单行注释 /* 多行注释 */ /** 文档注释 */
-
在控制台输出:
System.out.println();
-
在控制台输入:
import java.util.Scanner;
Scanner sc = new Scanner(System.in); XXX i = sc.nextXXX(); //看具体要接收什么,如,字符串就next(),整数就nextInt(),浮点数就nextDouble()
-
导包:
如果类在
java.lang
包下,则不需要导包,否则需要使用import
语句进行导入。
-
-
标识符的命名:
-
语法限制规则:
-
由数字、字母、下划线(_)和美元符($)组成;
-
不能以数字开头;
-
不能是关键字;
-
区分大小写;
-
-
规范约定:
-
小驼峰:(用于变量、属性、方法名)
- 标识符是一个单词的时候,首字母小写;
- 标识符由多个单词组成的时候,第一个单词首字母小写,其他单词首字母大写;
-
大驼峰:(用于类名)
- 标识符是一个单词的时候,首字母大写;
- 标识符由多个单词组成的时候,每个单词的首字母大写;
-
-
-
运算符:
-
逻辑运算符:
// 与或非 & | ! // 短路与或 && ||
-
短路与:当左边的为
false
时,结果为false
,右边直接不执行; -
短路或:当左边的为
true
时,结果为true
,右边直接不执行;
-
-
比较是否相等:
==
-
对于基本数据类型,比较的是它们的数据值是否相等;
-
对于引用数据类型,比较的是它们的地址是否相等;
-
-
对于字符型数据,也可以直接比较大小,使用的是它们的 ASCII 码值。
-
三元运算符:
首先计算关系表达式的值,如果值为
true
,表达式1的值就是运算结果,如果值为false
,表达式2的值就是运算结果。关系表达式 ? 表达式1 : 表达式2;
-
数据类型
-
数据类型:
数据大类 数据类型 内存占用字节数 表数范围(用相应包装类的常量属性可看) 整数 byte 1 -128~127 short 2 -215 ~ 215-1(约正负3.2万多) int 4 -231 ~ 231-1 (约正负 21亿多) long 8 -263 ~ 263-1(约正负922亿亿多) 浮点数 float 4 1.4x10-45 ~ 3.4x1038 double 8 4.9x10-324 ~ 1.8x10308 字符 char 2 0 ~ 65535 布尔 boolean 1 true,false **注意:**对于直接书写在代码中的数字,整数默认会被当作
int
类型存储,小数默认会被当作double
类型存储。- 若要指定其为
long
类型的,则在数字后加L
或l
; - 若要指定其为
float
类型的,则在数字后加F
或f
;
- 若要指定其为
-
变量的数据类型转换:
-
自动转换:
- 允许把一个表示数据范围小的数值或者变量赋值给另一个表示数据范围大的变量,数据的类型也就自动转成范围大的那个类型;
- 不同类型的数据/变量参与算术运算时,结果将自动转为这些变量/数据里面表数范围最大的那个类型;
-
强制转换:
把一个表示数据范围大的数值或者变量赋值给另一个表示数据范围小的变量,会损失精度,语法如下:
目标数据类型 变量名 = (目标数据类型) 值或者变量;
-
流程控制
-
if 语句:
if (关系表达式) { 语句体; }
if (关系表达式) { 语句体1; } else { 语句体2; }
if (关系表达式1) { 语句体1; } else if (关系表达式2) { 语句体2; } … else { 语句体n+1; }
-
switch 语句:
switch (表达式) { case 值1: 语句体1; break; case 值2: 语句体2; break; … default: 语句体n+1; [break;] }
switch (表达式) { case 值1: case 值2: ... 语句体1; break; case 值k: case 值k+1: 语句体2; break; … default: 语句体n+1; [break;] }
-
for 语句:
for (初始化语句;条件判断语句;条件控制语句) { 循环体语句; }
for 的死循环写法:
for (;;) { 循环体语句; }
-
while 语句:
初始化语句; while (条件判断语句) { 循环体语句; 条件控制语句; }
while 的死循环写法:
while (true) { 循环体语句; }
-
do…while 语句:
初始化语句; do { 循环体语句; 条件控制语句; } while (条件判断语句);
-
说明:
-
循环语法的区别:
-
for 循环和 while 循环先判断条件是否成立,然后决定是否执行循环体(先判断后执行);
-
do…while 循环先执行一次循环体,然后判断条件是否成立,是否继续执行循环体(先执行后判断);
-
-
跳转语句:
-
continue
用在循环中,基于条件控制,跳过某次循环体内容的执行,继续下一次的执行; -
break
用在循环中,基于条件控制,终止循环体内容的执行,也就是说结束当前的整个循环;
-
-
方法
-
声明语法基本结构:
修饰符 返回值类型 方法名(参数列表) { 方法体... }
-
支持可变参数列表:
修饰符 返回值类型 方法名(数据类型... 变量名) { ... }
- 传入的实参会被封装成对应类型的数组,此处的 “
变量名
” 即为数组引用名,在方法体中使用。
注意: 在语法要求上,若一个方法有多个参数,且其中包含可变参数,则可变参数必须放在形参列表最后面,如:
void add(int a, int... b) { ... }
- 传入的实参会被封装成对应类型的数组,此处的 “
-
方法重载:
- 方法名相同,而传入参数列表不同的方法声明即为方法重载;
- 对于返回值类型、修饰符等,忽略不看。
修饰符
-
权限修饰符:
被修饰符修饰的成员 本类的代码中 同一包中的类的代码中 不同包的子类的代码中 不同包的无关类的代码中 private 允许访问 缺省(包访问权限) 允许访问 允许访问 protected 允许访问 允许访问 允许访问 public 允许访问 允许访问 允许访问 允许访问 -
状态修饰符:
-
final
:“最终”,可以修饰变量、方法、类。- 修饰变量:表明该变量是常量,不能再次被赋值。
- 变量是基本类型:
final
修饰指的是基本类型的数据值不能发生改变; - 变量是引用类型:
final
修饰指的是引用类型的地址值不能发生改变,但是地址里面的内容是可以发生改变的;
- 变量是基本类型:
- 修饰方法:表明该方法是最终方法,不能被重写。
- 修饰类:表明该类是最终类,不能被继承。
- 修饰变量:表明该变量是常量,不能再次被赋值。
-
static
:“静态”,可以修饰成员方法、变量。-
绑定“类”本身,为此类的所有对象所共享,可以通过类名调用(推荐)
-
访问特点:
- 动态成员方法可以访问静态成员
- 而静态成员方法只能访问静态成员
-
-
数组
定义与使用
-
数组是一种用于存储多个相同类型数据的存储模型。
-
声明语法:
//格式1: 数据类型[] 变量名; //格式2: 数据类型 变量名[];
-
操作方法:
-
通过索引访问元素;
-
length
属性存储数组的长度;Java 中数组一经创建,长度不可修改。
-
初始化
Java 中的数组必须先初始化,然后才能使用。而所谓初始化,就是为数组元素分配内存空间,并为每个数组元素赋值。
-
静态初始化:
数据类型[] 变量名 = {数据1, 数据2, ...};
-
动态初始化:
数据类型[] 变量名 = new 数据类型[数组长度];
- 初始化时只指定数组长度,则系统会为数组元素分配初始值(
0
,false
,null
)。
- 初始化时只指定数组长度,则系统会为数组元素分配初始值(
面向对象特性问题
-
数组要求元素的类型必须相同,而在存在继承关系的情况下,使用静态初始化和动态初始化两种方式,得出的这个数组的“基准相同类型”是不一样的,后续只有这个“基准相同类型”及其子类的对象能被 set 进数组(索引访问更改某位置的元素)。
以下举例的代码中:
class B extends A
。-
静态初始化的方式下,由被赋值的数组引用变量的类型决定:
public static void main(String[] args) { A[] as1 = {new A(), new A()}; as1[0] = new A(); // OK as1[1] = new B(); // OK A[] as2 = {new A(), new B()}; as2[0] = new B(); // OK A[] as3 = {new B(), new B()}; as3[0] = new A(); // OK B[] bs1 = {new B(), new B()}; bs1[0] = new A(); // not OK! B[] bs2 = {new A(), new B()}; // not OK! }
-
动态初始化的方式下,由
new
关键字后的类型决定:public static void main(String[] args) { A[] as1 = new A[2]; as1[0] = new A(); // OK as1[1] = new B(); // OK B[] bs1 = new B[2]; bs1[0] = new B(); bs1[1] = new A(); // not OK ! B[] bs2 = new A[2]; // not OK ! A[] as2 = new B[2]; // OK ! as2[0] = new B(); // OK ! as2[1] = new A(); // compile OK & runtime not OK !!! }
-
其中,以上两点的重点区别体现在:
public static void main(String[] args) { A[] as3 = {new B(), new B()}; as3[0] = new A(); // OK A[] as2 = new B[2]; as2[0] = new B(); // OK ! as2[1] = new A(); // compile OK & runtime not OK !!! }
-
-
为了让元素方便地使用 Java 的多态特性,数组支持一个特殊的语法——允许把数组对象赋值给其元素的基类的数组引用,并且,用这个基类数组引用配合索引时,代表的是相应元素的基类引用。
这个本身看起来是离谱的,因为数组对象是数组对象,即是一个容器对象自身,用它持有的对象的继承关系来把容器对象自身赋值给其持有对象的基类数组引用,在逻辑上完全不合理,但 Java 最初就是这么设计的。。。后来是通过集合配合泛型,重新设计了这种用法才在逻辑上合理地解决了问题。
- 对于动态初始化的情况,从一开始就明确了数组的“基准相同类型”;
- 对于静态初始化的情况,对于
{数据1, 数据2, ...}
,Java 不允许它直接放入方法调用的实参列表中,必须赋值给一个确定类型的数组引用,再用这个数组引用作为实际参数传入方法中,因为它的“基准相同类型”只有这样才能确定;
举例如下:
public class A { String mbr = "A's mbr"; void a() { System.out.println("A's a-method"); } } public class B extends A { String mbr = "B's mbr"; void a() { System.out.println("B's overriding a-method"); } void b() { System.out.println("B's b-method"); } }
public class JavaSE { static void show(A[] as) { for (A a : as) { System.out.print("属性:" + a.mbr + " | 方法:"); a.a(); } System.out.println("-----------"); } public static void main(String[] args) { A[] as1 = {new A(), new B()}; show(as1); /* 控制台输出如下: 属性:A's mbr | 方法:A's a-method 属性:A's mbr | 方法:B's overriding a-method ----------- */ A[] as2 = new A[2]; as2[0] = new A(); as2[1] = new B(); show(as2); /* 控制台输出如下: 属性:A's mbr | 方法:A's a-method 属性:A's mbr | 方法:B's overriding a-method ----------- */ A[] as3 = {new B(), new B()}; show(as3); /* 控制台输出如下: 属性:A's mbr | 方法:B's overriding a-method 属性:A's mbr | 方法:B's overriding a-method ----------- */ A[] as4 = new B[2]; as4[0] = new B(); as4[1] = new B(); show(as4); /* 控制台输出如下: 属性:A's mbr | 方法:B's overriding a-method 属性:A's mbr | 方法:B's overriding a-method ----------- */ B[] bs1 = {new B(), new B()}; show(bs1); /* 控制台输出如下: 属性:A's mbr | 方法:B's overriding a-method 属性:A's mbr | 方法:B's overriding a-method ----------- */ B[] bs2 = new B[2]; bs2[0] = new B(); bs2[1] = new B(); show(bs2); /* 控制台输出如下: 属性:A's mbr | 方法:B's overriding a-method 属性:A's mbr | 方法:B's overriding a-method ----------- */ B[] bs3 = {new B(), new B()}; System.out.print(bs3[0].mbr + " | "); bs3[0].a(); bs3[0].b(); /* 控制台输出如下: B's mbr | B's overriding a-method B's b-method */ } }