Java 基础知识
程序设计基础知识
1.计算机的原理
-
冯诺依曼体积结构
主要思想:①二进制 ②存储程序式
-
输入(输出)设备输入(输出)的数据到存储器(内存)
比如:C语言中输入的数据不是从键盘上得来,是从输入缓冲区(内存中)
-
运算器:算数运算/逻辑运算(将数据从运算器ALU送到寄存器中,运算完成后再送回ALU)
-
控制器:
- 指令译码器(ID:instruction decoder):最重要的部分,ID从IR中读取指令进行翻译
- 指令寄存器(IR:instruction register)
- 程序计数器(PC:program counter):PC知道下一条指令指哪,if/else/for都是在改变PC中的内容
-
存储器:存放内存条
内存:
- 代码区(text)
- 数据区:
- 静态数据区:常量/全局变量
- 堆区:long数据
- 栈区:局部变量/函数的参数(自动分配、回收内存)
2.数据的表示
计算机中,所有数据都以二进制的形式存储和处理
3.Java符号集
-
标识符
数字、字母、下划线组成(数字不可作为开头)
与C相同
-
关键字
-
运算符
-
分隔符
-
注释
4.数据类型
基本数据类型
Java中定义了8种基本数据类型
-
整数——byte、short、int、long
Java中没有无符号(unsigned)整数
关于Java无符号数的讨论:
http://www.darksleep.com/player/JavaAndUnsignedTypes.html
?How do we work around this lack of unsigned types?
The answer is, you use the signed types that are larger than the original unsigned type. I.e. use a short to hold an unsigned byte, use a long to hold an unsigned int. And use a char to hold an unsigned short.
-
四种表示方式:十进制、八进制、十六进制、二进制
2 十进制整数2
077 以0开头表示八进制值
0xBAAC 以0x开头表示十六进制值
0b10001 以0b开头表示二进制值
-
默认是int类型
-
使用字母L或l表示long类型
public class text { public static void main(String[] args) { int i1=0B1000_0000_0000_0000_0000_0000_0000_0011; System.out.println(i1); System.out.println(0b1110_1001); byte b1=(byte)0b1110_1001; System.out.println(b1); } } //运行结果: -2147483645 233 -23
计算机中所有整数都是以补码的形式储存的
在直接输出0b1110_1001时,默认其为int型整数,占4个byte(32bit),前面的省略24个0,所以其最高位(符号位)为0,为正数233。
但是将其强制转化为byte类型后,由于byte型整数占1个byte(8bit)所以最高位的1即为符号位,为负数-23
-
-
字符——char
-
浮点——double、float
-
布尔——boolean
以上8种数据类型所占的字节数:
- byte:1byte=8bit
- short:2byte=16bit
- int:4byte=32bit
- long:8byte=64bit
- char:2byte=16bit
- float:4byte=32bit
- double:8byte=64bit
- boolean:1byte=8bit
5.数据在计算机中的储存与运算
public class text {
public static void main(String[] args) {
byte x=(byte)(Byte.MAX_VALUE+1);
System.out.println(x);
}
}
//运行结果:
-128
Byte.MAX_VALUE表示byte类型数据的最大值,即127
字节类型和int类型做加法首先将字节类型转化成int类型,所以在不加强制类型转换时,等号右边是int类型
强制类型转换时,大数据类型转换为小数据类型时取尾数,如上题所示,将int类型(32bit)的数据强行压缩为byte类型(8bit),则取最后八位作为转换后的结果
6.字符类型-char
char:代表16位的Unicode字符,占2个字符
(注:C语言中char占1byte)
ASCLL码表:
- 字符必须再单引号(‘’)内
- 使用:
- ‘a’:字母a
- ‘\t’:制表符
- ‘\u???’:指定的Unicode字符(???代表4位16进制数)
public class text {
public static void main(String[] args) {
char ch1='A';
char ch2='\u0041';
char ch3=0X41;
char ch4=65;
int x='A';
System.out.println(ch1);
System.out.println(ch2);
System.out.println(ch3);
System.out.println(ch4);
System.out.println(x);
System.out.println("\u5f90\u6d77\u5b66\u9662");
//Unicode的字符编码,一般在字节码中会有这样的编译码
System.out.println("徐海学院");
}
}
//运行结果:
A
A
A
A
65
徐海学院
徐海学院
变量ch1,ch2,ch3,ch4中存放的都是数值65(书写形式不相同),对应ASCLL码的符号是’A’,即内存中是没有’A’的,只是在内存中存放65这样一个机内码。(最后输出结果是’A’靠的是字形码)
转义字符便于程序员的使用,比如:\n(换行符)不便于直接在代码中打出,这时转义字符\n就相应地起到了作用
7.浮点型-float/double
-
float-32位/double-64位
-
浮点数表示形式:
- E或e(加指数值)——科学计数法形式(double)
- F或f(float)
- D或d(double)
如:3.14:一个简单的浮点值(一个double类型)
6.02E23:一个大浮点值(6.02*1023)
2.718F:一个简单的float值
123.4E+306D:一个带有D(多余)的大的double值
-
默认类型是double(float f=3.14; //会报错)
public class text {
public static void main(String[] args) {
float a=1-0.9f;
System.out.println(a);
}
}
//运行结果:
0.100000024
不论是float还是double都是浮点数,而计算机是二进制的,浮点数会失去一定的精度。
★注★根本原因是:十进制值通常没有完全相同的二进制表示形式;十进制数的二进制表示形式可能不精确,只能无限接近于那个值
在计算机中可以用BigDecimal类型来精确表示小数
8.布尔类型-boolean
-
boolean数据类型有两个值:true和false
-
与C++/C不同的是,Java中没有bool关键字
-
例如:语句
boolean truth=true;
声明变量truth为boolean类型,并将其赋值为true
⚠注意:不能写为 boolean truth=1;
常见的错误变量声明
public class text {
public static void main(String[] args) {
float f=3.4;
//正确:float f=3.4f;
short s=1;
//易产生误解,此处为正确写法,为short类型变量赋值,编译器可以自动进行类型转换
s=s+1;
//正确:s=(short)(s+1);
boolean b=1;
//正确:boolean b=true;
double d=2e0.5;
//double类型的指数部分不可用小数,只能是整数,如:double d=2e5;
}
}
9.字符串类型-String
String
-
不是基本数据类型,而是一个类(class)
-
字符在双引号(“”)内,如:“Hello,World!”
-
与C语言不同的是,String中的S必须大写
-
与C语言不同的是,String不以’\0’结尾
-
使用如下:
-
声明两个String变量:String str1, str2;
-
声明两个String变量并且初始化它们:
String greeting="Good Morning!"; String errorMessage="Record Not Found!";
-
public class text {
public static void main(String[] args) {
String s1="abcd";
String s2="efgh";
System.out.println(s1+s2);
}
}
//运行结果:
abcefgh
字符串的连接运算:该处"+"用作连接两个字符串
public class text {
public static void main(String[] args) {
String s="abcd";
System.out.println(s+123);
}
}
//运行结果:
abcd123
字符串与一个整数相加,就会将整数转换成字符串再相加
public class text {
public static void main(String[] args) {
String s="456";
System.out.println(s+123);
System.out.println(123+s);
}
}
//运行结果:
456123
123456
加号两边只要有一个字符串,运行出来就是字符串相加
public class text {
public static void main(String[] args) {
String s="111";
System.out.println(123+100+s);
}
}
//运行结果:
223111
第一个加号左右两边做算数运算,第二个加号两边做字符串运算
public class text {
public static void main(String[] args) {
String s1="计算机"+1+8+"级";
String s2=1+8+"级"+"计算机";
String s3=""+1+8+"级"+"计算机";
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
}
}
//运算结果:
计算机18级
9级计算机
18级计算机
字符串连接运算符 +
- 执行String连接
- 产生新的String
- 一个参数必须是String对象
- 非字符串对象会自动转换为String对象,如果要转换为等价 的字符串时,使用toString()成员方法
记录字符串的长度
import java.util.Scanner;
public class text {
public static void main(String[] args) {
String s1="Java程序设计", s2="Java";
System.out.println(s1.length());
System.out.println(s2.length());
}
}
//运行结果:
8
4
一个英文字母占一个字节,一个汉字占两个字节
length统计的是字符的数量,而不是字节数
10.类型转换(Type Cast)
自动类型转换(隐式转换)
-
算数运算中,会先转换为高数据类型,再运算,结果也是最高位的数据类型
-
如果采用+=、*=等缩略形式的运算符,系统会自动强制将运算结果转换为目标变量的类型
-
当运算符为自动递增运算符(++)或自动递增运算符(–)时,如果操作数为byte,short,或char,类型不发生改变
-
⚠注意:short和char不能相互进行自动类型转换(有需要的话可以进行强制类型转换)
原因:short是有符号整型(-32768—32767),char是无符号整型(0—65535),short类型转换为char类型的时候会出现精度丢失
-
在基本数据类型中,byte、short、int、char均为整型,float、double为浮点型。整型都是用补码表示(表示的范围大小就是看所占字节数大小);而浮点型的数表示起来是一部分储存尾数部分,一部分表示指数部分,比如浮点数0.123*1050在计算机中的
储存:+123存在尾数部分,50存在指数部分,这就导致浮点数可以存储的数值可以远远大于整型可以存储的数值,所以浮点型的取值范围大于整型的取值范围
public class text {
public static void main(String[] args) {
short a=1;
a=a+1;//报错:不可将int自动转化为short类型
//修正:
a=(short)(a+1);//正常运行(高类型数据向低类型数据转换可以进行强制转换)
int b=a+1;//正常运行:自动类型转换只可以将低类型数据转换为高类型数据
a++;//正常运行
a+=1;//正常运行
}
}
强制类型转换
语法:(数据类型)数值
public class text {
public static void main(String[] args) {
double finalPrice=3.65;
int price=(int)finalPrice;
//此处的变量finalPrice依然是double类型,仅仅是数值由3.65变为3
System.out.println(price);
}
}
//运行结果:
3
将double类型转换为int类型就是将位数部分地小数去掉,(double占8个字节,int占4个字节,强制将8个字节的数值放到4个字节中自然是要舍弃一部分内容的)
***(int)***是在做一步运算的处理(可以将其看做一个运算符),在Java中是不可省略的,明确告诉你,这里会出现一部分精度的丢失,相当于手术前签字确认的作用
11.Scanner类
-
导入包
import java.util.Scanner;
-
创建Scanner对象
Scanner sc=new Scanner(System.in);
-
通过对象获取数据
int x=sc.nextInt();
//输入一个int类型的整数并对应输出
import java.util.Scanner;
public class text {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);//在此处占用资源,此处sc为Scanner类的一个对象
int a=sc.nextInt();//输入int类型的整数,在next后加上Int
sc.close();//占用完资源后及时关闭资源避免警告
System.out.println(a);
}
}
//样例输入:
321321
//样例输出:
321321
使用scanner(system.in)时,使用完毕后,一定要关闭扫描器,之所以要关闭Scanner是因为System.in在被第一次声明时会打开InputStream,而输入流是非常占据内存的,为此我们通常为了节省内存考虑会选择调用Scanner.close()来关闭输入流,从而节省内存。如果不写sc.close(),尽管sc同样会在一段时间后被回收,但如果Scanner所在的函数被大量重复调用的话,就会导致一定程度的内存浪费。
如果在代码中关闭了Scanner,那么在接下来的代码中将无法再使用Scanner,即使重新创建一个Scanner类对象并赋值给sc也是如此:package Test; import java.util.Scanner; public class Test { public static void main(String[] args) { Scanner sc=new Scanner(System.in); int a=sc.nextInt(); System.out.println(a); sc.close(); sc=new Scanner(System.in); int b=sc.nextInt(); System.out.println(b); sc.close(); } } //报错:java.util.NoSuchElementException
package Test; import java.util.Scanner; public class Test { public static void main(String[] args) { Scanner sc=new Scanner(System.in); int a=sc.nextInt(); System.out.println(a); sc.close(); Scanner sc1=new Scanner(System.in); int b=sc1.nextInt(); System.out.println(b); sc1.close(); } } //报错:java.util.NoSuchElementException
//输入一个字符串并对应输出
import java.util.Scanner;
public class text {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
String a=sc.next();//输入字符串,next后无需添加内容
sc.close();
System.out.println(a);
}
}
//样例输入:
世界和平
//样例输出:
世界和平
带空格的字符串输入
- 对于带空格的字符串输入,若依然使用String s=sc.next();则只能输出第一个空格前的字符串
import java.util.Scanner;
public class text {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
String s=sc.next();
sc.close();
System.out.println(s);
}
}
//样例输入:
I love you!
//样例输出:
I
- 对于带空格的字符串,应使用String s=sc.nextLine();
import java.util.Scanner;
public class text {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
String s=sc.nextLine();
sc.close();
System.out.println(s);
}
}
//样例输入:
I love you!
//样例输出:
I love you!
例题:键盘输入2个数,输出大者
import java.util.Scanner;
public class text {
public static void main(String[] args) {
int a, b;
Scanner sc=new Scanner (System.in);
a=sc.nextInt();
b=sc.nextInt();
sc.close();
System.out.println(a>=b?a:b);//三目运算符
}
}
12.运算符和表达式
-
算数运算符
+、-、*、/、%、++、–
public class text { public static void main(String[] args) { int a=2, b=1, c, d; c=a+++b; d=++a+b; System.out.println(c); System.out.println(d); } } //运行结果: 3 5
注意++a和a++的区别:c=a+++b先将a带入表达式计算,再执行a+=1,而++a先执行a+=1,再将a带入表达式计算
关于c=a+++b;的断句问题:
**结合性问题:**a+++b;从右往左依次结合最多的符号优先结合;所以此处的断句为:(a++)+b;
如果想将a与++b相加应该写为:a+(++b);
-
赋值运算符
=、+=、-=、*=、/=、%=
-
关系运算符
==、!=、>、>=、<、<=
-
逻辑运算符
与C略有不同:Java中提供两种逻辑运算
&、|、^、!
短路逻辑运算符:&&、||
①a&&b只有a为真时,才需要判断b的值,如果a为假,就不必判断b的值,表达式的结果始终为假,则b被短路
②a||b只有a为假时,才需要判断b的值,如果a为真,就不必判断b的值,表达式的结果始终为真,则b被短路
public class text { public static void main(String[] args) { int x=1, y=1; if ((++x>0)&&(--y)>0) System.out.println("TRUE"); else System.out.println("FALSE"); System.out.println(x+","+y); } } //运行结果: FALSE 2,0 //对比: public class text { public static void main(String[] args) { int x=1, y=1; if ((++x>5)&&(--y)>0)//--y被短路 System.out.println("TRUE"); else System.out.println("FALSE"); System.out.println(x+","+y); } } //运行结果: FALSE 2,1
-
位运算符
&、|、^、~、>>、<<<、<<
<<表示左移,不分正负数,低位补0 >>表示右移,如果该数为正,则最高位补0,若为负,则最高位补1 >>>表示无符号右移,也叫逻辑右移,即若该数为正数,则高位补0,若该数为负数,则右移后高位同样补0(不考虑高位的正负号,正数的>>>等同于>>) ☆巧记:箭头指向左,表示左移,箭头指向右表示右移
public class text { public static void main(String[] args) { int a=4;//100 int b=5;//101 System.out.println("a&b="+(a&b)); System.out.println("a|b="+(a|b)); System.out.println("a^b="+(a^b)); System.out.println("~a="+(~a)); System.out.println("a>>1="+(a>>1));//a右1位,即a/(2^1) System.out.println("a<<3="+(a<<3));//a左移3位,即a*(2^3) System.out.println("a>>>1="+(a>>>1)); } } //运行结果: a&b=4 a|b=5 a^b=1 ~a=-5 a>>1=2 a<<3=32 a>>>1=2
-
三目(元)运算符
(关系表达式)?表达式1:表达式2
public class text { public static void main(String[] args) { System.out.println(true?Integer.valueOf(1):Double.valueOf(2)); } } //运行结果: 1.0
三目运算符表达式的结果数据类型由三目运算符表达式1和表达式2中最大的数据类型决定
关系表达式一定只能是Boolean类型
-
对象判定运算符
★instance of★
-
字符串连接运算符
-
点运算符
.
用于访问类/对象成员,包括属性和方法
- <对象>.<成员>
- <类名>.<静态成员>
课堂练习
-
public class text { public static void main(String[] args) { short a, b, c; a=1; b=2; c=a+b; a+=2; } }
★以上代码中错误的语句是
A.a=1;
B.b=2;
C.c=a+b;(√)
D.a+=2;
**解析:**c是short类型,而a+b是int类型,系统不能将int型自动转换成short型,所以C错误
⚠问:为什么a和b都是short型而a+b就变成了int型?
⚠答:运算器只能做两种数据的运算:32位数和64位,而short类型、char类型、byte类型只有8位、16位,所以从硬件结构上来说就需要把它转成32位,再让CPU的运算器去做,所以运算会提升数据类型,即物理结构就决定了运算方式。即char、short、int及枚举类型在参与运算的时候,都会转换成int类型进行运算。所以即使a、b都是short类型但是在参与运算的时候都转变为了int类型。
-
下列哪项不是int类型的字面量
A.\u03A6(√)
B.077
C.0xABBC
D.20
**解析:**A选项的"\u03A6"是一个char类型
public class text { public static void main(String[] args) { int a='\u03A6';//自动类型转换 System.out.println(a); char b='\u03A6'; System.out.println(b); } } //运行结果: 934 Φ
在上述代码中仍可以输出一个正常数值是因为系统自动进行了自动类型转换,而此处"\u03A6"仍是字符类型
*13.其他输入方式
System相关用法
import java.io.IOException;
public class text {
public static void main(String[] args) throws IOException {
char c;
System.out.print("Please input a char:");
c=(char)System.in.read();
//从标准输入读入u一个字节的信息,并返回一盒字符型变量
System.out.println("Receive char =" + c);
}
}
//样例输入:
w
//样例输出:
w
以上代码可以从键盘中读取一个字符,但是只能读取第一个,不管输入多少,只能读取第一个
import java.io.IOException;
public class text {
public static void main(String[] args) throws IOException {
int c;
System.out.print("Please input a Integer:");
c = (int) System.in.read();
//从标准输入读入u一个字节的信息,并返回一盒字符型变量
System.out.println(c);
}
}
//样例输入:3
//样例输出:51
使用这种方法进行输入时,会因为你的键盘输入习惯等问题对结果造成影响,而且,返回值始终为ASCⅡ码
JOptionPane相关功能
这一种输入方法和之前三种输入输出的形式都有所不同,他是会在执行操作的时候,弹出一个弹框,所有的输入输出都需要从弹框中输入显示。
1.显示输入消息框,可以输入数据
String str1 = JOptionPane.showInputDialog(“输入消息框”,“0”);
2.显示出一个弹框
null表示对话框显示在屏幕中间
第二个参数表示要显示的字符结果
JOptionPane.showMessageDialog(null,str1);
JOptionPane.showMessageDialog(null,“a + b =” + c);
一个很简单的代码,用来做加减乘除运算:
import javax.swing.JOptionPane;
public class text {
public static void main (String[] args) {
double a,b;
String str1 = JOptionPane.showInputDialog("输入a的值","0");
//由于这个方法输入的格式为字符型,所以我们要转换成整型
a = Integer.parseInt(str1);
String str2 = JOptionPane.showInputDialog("输入运算符号","+");
String str3 = JOptionPane.showInputDialog("输入b的值","0");
b = Integer.parseInt(str3);
double c = 0;
if (str2.equals("+") )
c = a + b;
if (str2.equals("-"))
//或者是str2.contains("-")
c = a - b;
if (str2.equals("*") )
c = a * b;
if (str2.equals("/") )
c = a / b;
JOptionPane.showMessageDialog(null, c);
}
}
14.输出
System.out.println();
带换行的输出
public class text {
public static void main(String[] args) {
System.out.println("喜羊羊与灰太狼");
}
}
//运行结果:
喜羊羊与灰太狼
System.out.print();
不带换行的输出
public class text {
public static void main(String[] args) {
System.out.print("徐海学院");
}
}
//运行结果:
喜羊羊与灰太狼
System.out.printf();
同C语言一样的格式化输出
public class text {
public static void main(String[] args) {
int a=3;
System.out.printf("%d", a);
}
}
//运行结果:
3
作业
-
从键盘上输入一个月份数,输出该月份代表的季节(春季、夏季、秋季、冬季)
//正确1 import java.util.Scanner; public class text { public static void main(String[] args) { Scanner sc=new Scanner(System.in); int x=sc.nextInt(); sc.close(); if (x==2||x==3||x==4) System.out.println("Spring"); else if (x==5||x==6||x==7) System.out.println("Summer"); else if (x==8||x==9||x==10) System.out.println("Autumn"); else if (x==11||x==12||x==1) System.out.println("winter"); else System.out.println("Not found") } } //错误 import java.util.Scanner; public class text { public static void main(String[] args) { Scanner sc=new Scanner(System.in); String s=sc.next(); sc.close(); if (s=="November"||s=="December"||s=="January") System.out.println("Winter"); else if (s=="February"||s=="March"||s=="April") System.out.println("Spring"); else if (s=="May"||s=="June"||s=="July") System.out.println("Summer"); else if (s=="August"||s=="September"||s=="october") System.out.println("Autumn"); else System.out.println("Not found"); } } //错误原因: //字符串比较需要用equals()方法(不能使用 ==): String s=sc.next(); if(s.equals("abc")) System.out.println("..."); //订正 import java.util.Scanner; public class text { public static void main(String[] args) { Scanner sc=new Scanner(System.in); String s=sc.next(); sc.close(); if (s.equals("November")||s.equals("December")||s.equals("January")) System.out.println("Winter"); else if (s.equals("February")||s.equals("March")||s.equals("April")) System.out.println("Spring"); else if (s.equals("May")||s.equals("June")||s.equals("July")) System.out.println("Summer"); else if (s.equals("August")||s.equals("September")||s.equals("october")) System.out.println("Autumn"); else System.out.println("Not found"); } }
-
比较三个数的大小,按照从小到大的顺序输出
import java.util.Scanner; public class text { public static void main(String[] args) { Scanner sc=new Scanner(System.in); int a=sc.nextInt(), b=sc.nextInt(), c=sc.nextInt(), t; sc.close(); if (c<a) { t=c;c=a;a=t; } if (c<b) { t=c;c=b;b=t; } if (b<a) { t=b;b=a;a=t; } System.out.println(a+" "+b+" "+c); } }
-
输出10-50之间所有能被3整除的数
public class text { public static void main(String[] args) { int i; for (i=10; i<=50; ++i) if (i%3==0) System.out.println(i); } }
Java程序结构
1.流程控制
-
分支结构(顺序结构:从上到下依次执行)
-
if else
if/else语句应包含所有情况的,保证逻辑的完整
-
switch case
-
-
循环结构
- for
- while
思路
①起止条件/次数(从×××开始,到×××结束)
②循环不变式(重复做的事情):形式不变,内容在变
- for
- while
例题1:求 1-1/2+2/3-3/4……(n-1)/n。
import java.util.Scanner;
public class Test {
public static void main (String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
double i, sum=1;
sc.close();
for (i=2; i<=n; ++i) {
if (i%2==0) sum-=(i-1)/i;
else sum+=(i-1)/i;
}
System.out.println(sum);
}
例题2:使用控制台界面,实现菜单程序。
请输入数字1~3选择菜单项:
1.新建 2.打开 3.退出
1 你选择了新建
2 你选择了打开
1 你选择了新建
1 你选择了新建
3 你选择了退出,程序退出
import java.util.Scanner;
public class text {
public static void main(String[] args) {
System.out.println("请输入数字1~3选择菜单项:");
System.out.println("1.新建 2.打开 3.退出");
while (true) {
Scanner sc=new Scanner (System.in);
int x=sc.nextInt();
if (x==1) System.out.println("你选择了新建");
else if (x==2) System.out.println("你选择了打开");
else break;
}
System.out.println("你选择了退出,程序退出。");
}
}
例题3:输出Fibonacci数列的前n个数
//非数组法:
import java.util.Scanner;
public class Test {
public static void main (String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt(), i, x=1, y=1, ans=0;
sc.close();
System.out.print(1+" ");
if(n>=2) System.out.print(1+" ");
if(n>2) for (i=3; i<=n; ++i) {
ans=x+y;
System.out.print(ans+" ");
x=y;
y=ans;
}
}
}
//数组法:
import java.util.Scanner;
public class Test {
public static void main (String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt(), i;
sc.close();
int[] a=new int[n];
if(n==1) a[0]=1;
else a[0]=a[1]=1;
for (i=2; i<n; ++i)
a[i]=a[i-1]+a[i-2];
for (i=0; i<n; ++i)
System.out.print(a[i]+" ");
}
}
例题4:输入行数(比如输入5),输出如下图形:
*
**
***
****
*****
import java.util.Scanner;
public class Test {
public static void main (String[] args) {
System.out.println("请确定三角形的行数n:");
Scanner sc=new Scanner(System.in);
int n=sc.nextInt(), i, j;
sc.close();
for (i=1; i<=n; ++i) {
for (j=1; j<=n-i; ++j)
System.out.print(" ");
for (j=1; j<=i; ++j)
System.out.print("*");
System.out.println("");
}
}
}
作业
-
输入正整数n,输出从1到n中不能同时被3和7整除的数字。
import java.util.Scanner; public class zhengchu { public static void main(String[] args) { System.out.println("请输入整数n:"); Scanner sc=new Scanner(System.in); int n, i; n=sc.nextInt(); sc.close(); System.out.println("1~n中不能同时被3和7整除的数:"); for (i=1; i<=n; ++i) if (i%21!=0) System.out.print(i+" "); } }
-
输入若干整数,输出最小值和最大值。
import java.util.Scanner; public class minmax { public static void main (String[] args) { System.out.println("请输入若干整数(以0结束):"); Scanner sc=new Scanner(System.in); int x, max=0, min=2147483647; while(true) { x=sc.nextInt(); if (x==0) break; if (x>max) max=x; else if (x<min) min=x; } sc.close(); System.out.println("最大值:"+max); System.out.println("最小值:"+min); } }
-
给定两个整数,计算最大公约数(GCD)。使用欧几里得(Euclid)算法(自己上网找思路,不是找现成的代码)。
import java.util.Scanner; public class gcd { public static void main (String[] args) { System.out.println("请输入两个整数x和y:"); Scanner sc=new Scanner(System.in); int x, y, r; x=sc.nextInt(); y=sc.nextInt(); sc.close(); while (y>0) { r=x%y; x=y; y=r; } System.out.println("x和y的最大公约数是:"); System.out.println(x); } }
-
输入正整数n(n<20),输出如下矩阵:
输入样例:3 输出样例: 1 2 3 2 3 4 3 4 5
//数组法: import java.util.Scanner; public class juzhen1 { public static void main (String[] args) { System.out.println("请输入正整数n:"); int[][] a=new int[27][27]; Scanner sc=new Scanner(System.in); int n, i, j; n=sc.nextInt(); sc.close(); for (i=1; i<=n; ++i) { for (j=1; j<=n; ++j) { a[i][j]=i+j-1; System.out.print(a[i][j]+" "); } System.out.println(""); } } }
//非数组法: public class juzhen2 { public static void main(String[] args) { System.out.println("请输入正整数n:"); Scanner sc=new Scanner(System.in); int n, i, j; n=sc.nextInt(); sc.close(); for (i=1; i<=n; ++i) { for (j=i; j<=n+i-1; ++j) System.out.print(j+" "); System.out.println(""); } } }
-
输入一个数,判断是否为素数。
//非函数: import java.util.Scanner; public class Test { public static void main (String[] args) { Scanner sc=new Scanner(System.in); int x=sc.nextInt(), i, flag=0; sc.close(); if (x==1) { System.out.println("NO"); flag=1; } else for (i=2; i*i<=x; ++i) if (x%i==0) { System.out.println("NO"); flag=1; } if (flag==0) System.out.println("YES"); } }
//函数: import java.util.Scanner; public class Test { public static void main (String[] args) { Scanner sc=new Scanner(System.in); int x=sc.nextInt(); sc.close(); if (isPrime(x)==true) System.out.println("YES"); else System.out.println("NO"); } public static boolean isPrime(int x) { int i; if (x==1) return false; for (i=2; i*i<=x; ++i) if (x%i==0) return false; return true; } }
-
求1-1000内所有素数的和。
public class Test { public static void main (String[] args) { int i, sum=0; for (i=1; i<=1000; ++i) if (isPrime(i)==true) sum+=i; System.out.println(sum); } public static boolean isPrime(int x) { if (x==1) return false; int i; for (i=2; i*i<=x; ++i) if (x%i==0) return false; return true; } }
-
输入n(n是奇数),输出下面的图形。比如n=7时,如下图所示:
* *** ***** ******* ***** *** *
import java.util.Scanner; public class lingxing { public static void main(String[] args) { System.out.println("请输入正整数n:"); int n, i, j; Scanner sc=new Scanner(System.in); n=sc.nextInt(); if (n%2==0) { System.out.printf("傻鸟!%d是奇数吗?!", n); return; } sc.close(); for (i=1; i<=(n+1)/2; ++i) { for (j=1; j<=(n+1)/2-i; ++j) System.out.print(" "); for (j=1; j<=i*2-1; ++j) System.out.print("*"); System.out.println(""); } for (i=(n+1)/2; i>0; --i) { if (i==1) continue; for (j=(n+1)/2-i+1; j>0; --j) System.out.print(" "); for (j=i*2-1-2; j>0; --j) System.out.print("*"); System.out.println(""); } } }
2.数组
数组是引用数据类型,不属于基本数据类型
-
定义
数据类型[] 数组名称=new 数组类型[num]; //这个num可以是个变量也可以是个数 int[] a=new int[5];
-
赋值
访问:数组名称[元素的索引] a[i]=5;//下标范围:0<=i<5
?为什么索引要从0开始?
索引其实就是"偏移量",就是相较于数组第一个元素的单位长度
-
静态初始化
数据类型[] 数组名称=new 数据类型[]{数组中的元素(用,隔开)} char[] a=new char[]{'h', 'e', 'l', 'l', 'o'}; //或者: char[] a={'h', 'e', 'l', 'l', 'o'};
注意:若使用第一种方法,则在初始化的时候不需要确定数组中元素的个数
-
length属性
a.length;获取元素个数
数组变量a中存放的是数组a的第一个元素的首地址,由new运算符返回
[]为下标运算符
例题:画出下列程序中变量的内存使用图
public static void main(String[] args) {
int[] a1 = new int[]{1,2,3,4,5};
int[] a2 = new int[] {6,7,8};
a2=a1;
a2[0]=100;
System.out.println(a1[0]);
System.out.println(a2[0]);
}
//运行结果:
100
100
数组常见的算法
-
遍历(Travel)
输出数组中的所有元素
-
for循环
public class Test { public static void main (String[] args) { int[] arr=new int[] {1, 2, 3 , 4, 5, 6}; int i; for (i=0; i<6; ++i) System.out.print(arr[i]+" "); } } //运行结果: 1 2 3 4 5 6
-
foreach循环
for(变量类型 变量名 : 数组名) { 含变量名的执行语句; }
不难看出,当使用foreach循环的时候,foreach中的循环变量相当于一个临时变量,系统会把数组元素依次赋给这个变量,而这个临时变量并不是数组元素,它只是保存了数组元素的值,因此如果希望改变数组元素的值,则不能使用这种foeeach循环。
public class Test { public static void main (String[] args) { int[] arr=new int[] {0, 2, 3, 4, 5, 6}; for (int i:arr) System.out.print(i+" "); } } //运行结果: 0 2 3 4 5 6
-
-
查找(Search)
找出数组中最大的元素
public class Test { public static void main (String[] args) { int[] arr=new int[] {2, 2, 2, 1, 2, 1, 7, 0}; int max=0; for (int i:arr) if (i>max) max=i; System.out.println(max); } } //运行结果: 7
-
逆置(Reverse)
将数组中的所有元素反向存放。即第一个元素放到最后,第二个元素放到倒数第二个。
public class Test { public static void main (String[] args) { int[] arr=new int[] {2, 2, 2, 1, 2, 1, 7, 0}; int i, t; for (i=0; i<4; ++i) { t=arr[i]; arr[i]=arr[7-i]; arr[7-i]=t; } for (i=0; i<8; ++i) System.out.print(arr[i]+" "); } } //运行结果: 0 7 1 2 1 2 2 2
在数组逆置的过程中只需要遍历数组长度的一半即可
-
排序(Sort)
冒泡排序、选择排序、插入排序
//冒泡排序: import java.util.Scanner; public class maopaopaixu { public static void main(String[] args) { try (Scanner sc = new Scanner(System.in)) { int n, i, j, t; System.out.println("请确定数组中的元素个数n:"); n=sc.nextInt(); int[] a=new int [n]; System.out.println("请输入数组中的n个元素:"); for (i=0; i<n; ++i) a[i]=sc.nextInt(); //进行冒泡排序 for (i=n-1; i!=0; --i) for (j=0; j<i; ++j) if (a[j+1]>a[j]) { t=a[j+1]; a[j+1]=a[j]; a[j]=t; } sc.close(); System.out.println("元素组中的数按从大到小排序:"); for (i=0; i<n; ++i) System.out.print(a[i]+" "); } } }
//选择排序: import java.util.Scanner; public class xuanzepaixu { public static void main(String[] args) { try (Scanner sc = new Scanner(System.in)) { int n, i, j, id, t; System.out.println("请确定数组中的元素个数n:"); n=sc.nextInt(); int[] a=new int [n]; System.out.println("请输入数组中的n个元素:"); for (i=0; i<n; ++i) a[i]=sc.nextInt(); //进行选择排序 for (i=0; i<=n; ++i) { id=i; for (j=i+1; j<n; ++j) if (a[j]>a[id]) id=j; if (id!=i) { t=a[id]; a[id]=a[i]; a[i]=t; } } sc.close(); System.out.println("元素组中的数按从大到小排序:"); for (i=0; i<n; ++i) System.out.print(a[i]+" "); } } }
-
数组转字符串
Arrays.toString(数组名称)
使用此方法可以将数组转换为字符串
import java.util.Arrays; public class Test { public static void main (String[] args) { int[] a=new int[]{2, 2, 2, 1, 2, 1, 7, 0}; String s=Arrays.toString(a); System.out.println(a); System.out.println(s); } } //运行结果: [I@4517d9a3 [2, 2, 2, 1, 2, 1, 7, 0]
import java.util.Arrays; public class Test { public static void main (String[] args) { char[] a=new char[]{'h', 'e', 'l', 'l', 'o'}; String s=Arrays.toString(a); System.out.println(a); System.out.println(s); } } //运行结果: hello [h, e, l, l, o]
⚠问:为何int型数组在输出数组名的时候输出的是收个元素的地址,而char型数组在输出数组名的时候就可以直接给出数组中的元素?
答:Java中print()方法对char[]类型做了特殊处理,该处理的效果即为直接输出字符数组的元素。
-
数组拷贝
- Arrays.copyOf(被拷贝的数组名称, 新数组的元素个数)
使用此方法来拷贝数组
import java.util.Arrays; public class Test { public static void main (String[] args) { int[] arr=new int[] {1, 2, 3 , 4, 5, 6}; System.out.println("arr="+Arrays.toString(arr)); int[] newarr1=Arrays.copyOf(arr, 3); System.out.println("newarr1="+Arrays.toString(newarr1)); int[] newarr2=Arrays.copyOf(arr, 8); System.out.println("newarr2="+Arrays.toString(newarr2)); } } //运行结果: arr=[1, 2, 3, 4, 5, 6] newarr1=[1, 2, 3] newarr2=[1, 2, 3, 4, 5, 6, 0, 0]
新数组的元素个数若是小于被拷贝的数组,则为部分拷贝,若是大于被拷贝的数组,则大于的部分用元素类型的默认值补充
- Arrays.copyOfRange(被拷贝数组名称,起点,终点)
使用这个方法拷贝数组可以做到区间拷贝,要注意的是这个区间是,左闭右开[起点,中点)
import java.util.Arrays; public class Test { public static void main (String[] args) { int[] arr=new int[] {1, 2, 3 , 4, 5, 6}; System.out.println("arr="+Arrays.toString(arr)); int[] newarr=Arrays.copyOfRange(arr, 2, 5); System.out.println("newarr="+Arrays.toString(newarr)); } } //运行结果: arr=[1, 2, 3, 4, 5, 6] newarr=[3, 4, 5]
对象数组
例题:下面这种数据表格在程序中如何存储?
学号 | 姓名 | 年龄 | 性别 |
---|---|---|---|
22160015 | 曹操 | 18 | 男 |
22160018 | 花木兰 | 19 | 女 |
22160089 | 李白 | 17 | 男 |
public class student {
String id, name, sex;
int age;
public student(String id, String name, String sex, int age) {
this.age=age;
this.id=id;
this.name=name;
this.sex=sex;
}
}
public class Test {
public static void main (String[] args) {
student[] students=new student[] {
new student("22160015", "曹操", "男", 18),
new student("22160018", "花木兰", "女", 19),
new student("22160089", "李白", "男", 17)
};
for (student i : students)
System.out.println(i.id+" "+i.name+" "+i.sex+" "+i.age);
}
}
//运行结果:
22160015 曹操 男 18
22160018 花木兰 女 19
22160089 李白 男 17
二维数组
-
矩阵数组
int[][] y=new int[2][3];
-
锯齿/交错数组
int[][] y=new int[2][]; y[0]=new int[3]; y[1]=new int[2];
作业
-
有序数组的二分查找算法的实现。
import java.util.Scanner; public class erfenchazhao { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("请确定数组中元素的个数n:"); int n=sc.nextInt(), i; int[] a=new int [n]; System.out.println("请输入数组中的n个元素:"); for (i=0; i<n ;++i) a[i]=sc.nextInt(); int left=0, right=n-1, target; System.out.println("请输入您想查找的对象:"); target=sc.nextInt(); sc.close(); while (left<=right) { int middle=left+(right-left)/2; if (a[middle]==target) { System.out.println("您所查找的对象在数组中的下标为:"); System.out.println(middle); break; } else if (a[middle]<target) left=middle+1; else if (a[middle]>target) right=middle-1; } if (left>right) System.out.println("Not Found!"); } }
-
从键盘输入一个正整数n,输出n行的杨辉三角。要求:使用二维交错/锯齿数组。
import java.util.Scanner; public class yanghuisanjiao { public static void main(String[] args) { Scanner sc=new Scanner(System.in); System.out.println("请输入正整数n:"); int n=sc.nextInt(), i, j; sc.close(); int[][] a=new int[n][]; for (i=0; i<n; ++i) a[i]=new int[i+1]; for (i=0; i<n; ++i) a[i][0]=a[i][i]=1; for (i=2; i<n; ++i) for (j=1; j<i; ++j) a[i][j]=a[i-1][j]+a[i-1][j-1]; System.out.println("杨辉三角前n行输出情况如下:"); for (i=0; i<n; ++i) { for (j=0; j<=i; ++j) System.out.printf("%-4d", a[i][j]); System.out.println(""); } } }
-
二维数组转置输出。(方形、矩形)
import java.util.Scanner; public class Test2 { public static void main(String[] args) { Scanner sc=new Scanner(System.in); System.out.println("请输入矩阵的行数row和列数col:"); int row=sc.nextInt(), col=sc.nextInt(); int[][] m=new int[row][col]; int i, j; System.out.printf("请输入%d行%d列的矩阵:\n", row, col); for (i=0; i<row; ++i) for (j=0; j<col; ++j) m[i][j]=sc.nextInt(); System.out.println("转置后的矩阵为:"); for (j=0; j<col; ++j) { for (i=0; i<row; ++i) System.out.print(m[i][j]+" "); System.out.print("\n"); } } }
3.方法(函数)method(function)
-
方法的定义
修饰符 返回值类型 方法名(参数类型 参数名1,参数类型 参数名2...){ 方法体语句; return 返回值; }
-
签名(signature)
方法的名称、参数数量、参数类型
注意:不包括返回值
-
形参和实参
参数传递是按对应“位置”结合而不是按名字结合
实参可以是表达式
参数传递-基本类型参数
程序代码:
import java.util.Scanner;
public class text {
public static void main(String[] args) {
int m, n;//局部变量只属于当前函数
m=15;
n=20;
changevalue(m, n);
changevalue(m, n);
System.out.println("m="+m+","+"n="+n);
}
public static void changevalue(int x, int y) {
x=x+20;
y=x+y;
System.out.println("x="+x+","+"y="+y);
}
}
//运行结果:
m=15,n=20
x=35,y=55
传参的过程:
①为形参申请内存空间
②将实参复制一份给形参
③执行方法体
④销毁形参的存储空间
Java程序中自动回收形式参数的存储空间
main函数特殊在它是程序的入口点
作用域指的是代码行(衡量空间的概念)
生命期:x、y在第7行调用的时候出生,11-15行代码执行结束,x、y死亡(衡量时间的概念)
参数传递-引用类型参数-数组
程序代码:
public static void main(String[] args) {
int[] M={15, 20};
changeValue(M);
System.out.println("M[0]="+M[0]+","+"M[1]="+M[1]);
}
public static void changeValue(int[] X) {
X[0]=X[0]+20;
X[1]=X[0]+X[1];
System.out.println("X[0]="+X[0]+","+"X[1]="+X[1]);
}
//运行结果:
X[0]=35,X[1]=55
M[0]=35,M[1]=55
传递过程:
①首先为形参分配内存空间
②将实参M的值传递给形参X,这时传递的是数组M首个元素的地址
③执行方法体的内容时,改变的是X通过地址找到的数组中的元素的值
④销毁形参存储空间即X
重载
构成方法重载的条件
- 重载的方法在同一个类中,方法名相同
- 签名不同:参数类型、个数、顺序至少有一个不同
class Calsum {
void sum(short i, short j) {
System.out.println("short:"+(i+j));
}
void sum(int i, int j) {
System.out.println("int:"+(i+j));
}
}
当传入不同数量或类型的参数时,再判断调用的是哪个方法
递归
递归:方法直接或间接调用自己
-
递归的应用场景
数据的定义是按递归定义的。(Fibonacci函数,n的阶乘)
问题解法按递归实现(回溯)
数据的结构形式是按递归定义的(二叉树的遍历、图的搜索)
-
递归的缺点
在递归调用的过程当中系统为每一层的返回点、局部变量等开辟了栈来存储,因此递归次数过多容易造成栈溢出
-
尾递归:就是操作的最后一步是调用自身的递归。Java中,建议将尾递归用循环改写
例题:使用递归,输出Fibonacci数列的第n个数
import java.util.Scanner;
public class Test2 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
System.out.print("你想要知道Fibonacci数列中的第几个数的值?");
int n=sc.nextInt();
sc.close();
System.out.printf("Fibonacci数列中第%d个数是:", n);
System.out.println(fib(n));
}
public static int fib(int x) {
if (x<=2) return 1;
else return fib(x-1)+fib(x-2);
}
}
思考:如何改成尾递归?(提示:把当前的运算结果或路径放在参数里传给下层函数)
作业
有序数组的二分查找算法的实现。要求:分别用循环和递归实现,并比较程序的差异。
//循环
import java.util.Scanner;
public class erfenchazhao {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请确定数组中元素的个数n:");
int n=sc.nextInt(), i;
int[] a=new int [n];
System.out.println("请输入数组中的n个元素:");
for (i=0; i<n ;++i) a[i]=sc.nextInt();
int left=0, right=n-1, target;
System.out.println("请输入您想查找的对象:");
target=sc.nextInt();
sc.close();
while (left<=right) {
int middle=left+(right-left)/2;
if (a[middle]==target) {
System.out.println("您所查找的对象在数组中的下标为:");
System.out.println(middle);
break;
}
else if (a[middle]<target) left=middle+1;
else if (a[middle]>target) right=middle-1;
}
if (left>right) System.out.println("Not Found!");
}
}
//递归
import java.util.Scanner;
public class Test2 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
System.out.println("请确定有序数组的元素个数:");
int n=sc.nextInt(), i;
int[] a=new int[n];
System.out.printf("请输入含%d个元素的有序数组:\n", n);
for (i=0; i<n; ++i)
a[i]=sc.nextInt();
System.out.println("你要查找的对象是:");
int x=sc.nextInt();
sc.close();
if(erfen(x, a, 0, n-1)>=0) {
System.out.printf("%d在数组中的下标是:\n", x);
System.out.println(erfen(x, a, 0, n-1));
}
else System.out.println("NOT FOUNT!");
}
public static int erfen(int x, int[] a, int l, int r) {
if (l>r) return -1;
int mid=l+(r-l)/2;
if(a[mid]==x) return mid;
else if(a[mid]>x) return erfen(x, a, l, mid-1);
else if(a[mid]<x) return erfen(x, a, mid+1, r);
else return -1;
}
}