回顾

编程学习时,软件密码不要设置的太过复杂,真正应用的时候再设置复杂的密码,

软件最后用免安装版本

建表

使用图形化工具==MySQLWorkbench.exe==进行建表,然后记录建表过程。

SQL语句

要了解增删改查,特别是查询

SQL语言的基本概念

基本表

基本表(BASE TABLE):是独立存在的表,不是由其它的表导出的表。一个关系对应一个基本表,一个或多个基本表对应一个存储文件。

视图

视图(VIEW):是一个==虚拟==的表,是从一个或几个基本表导出的表。它本身不独立存在于数据库中,数据库中只存放视图的定义而不存放视图对应的数据,这些数据仍存放在导出视图的基本表中。当基本表中的数据发生变化时,从视图中查询出来的数据也随之改变。

外模式 模式 内模式

SQL语言支持数据库的三级模式结构,如图3.1所示。其中

  • 外模式对应于视图和部分基本表,
  • 模式对应于基本表,
  • 内模式对应于存储文件。

image-20210718091401207

字段数据类型

数据库迁移很重要

不要使用存储过程,触发器,用的数据类型为int,varchar,其他数据库特有的数据类型尽量不要使用。

以主键形式保存的字段的数据类型使用char,不要使用varchar,这样索引主键比较方便。

text数据类型也是常用的,保存新闻,或者富文本编辑区保存的内容。

建立数据库

1
create database <数据库名>;

创建表示例

1
2
3
4
5
6
7
USE STUDENT
CREATE TABLE S
(SNO CHAR(8) ,
SN VARCHAR(20),
AGE INT,
SEX CHAR(2) DEFAULT '男' ,
DEPT VARCHAR(20));

一对一不要建表,把任意一段的主键,放到另一端作为外键

一对多不建表,把一的一端的主键,放到多的一端作为外键

多对多要建表,把两者的主键提取出来,放到关系表中作为外键,共同组成关系表的主键

索引后续会说

SQL数据查询

SELECT命令的格式与基本使用

数据查询是数据库中最常见的操作。

SQL语言提供SELECT语句,通过查询操作可得到所需的信息。

SELECT语句的一般格式为:

1
2
3
4
5
SELECT〈列名〉[{,〈列名〉}]
FROM〈表名或视图名〉[{,〈表名或视图名〉}]
[WHERE〈检索条件〉]
[GROUP BY <列名1>[HAVING <条件表达式>]]
[ORDER BY <列名2>[ASC|DESC]];

SELECT语句的格式:

1
2
3
4
5
6
7
8
9
SELECT	[ALL|DISTINCT][TOP N [PERCENT][WITH TIES]]
列名1 [AS 别名1]
[, 列名2 [ AS 别名2]…]
[INTO 新表名]
FROM 表名 1[[AS] 表1别名]
[INNER|RIGHT|FULL|OUTER][OUTER]JOIN
表名2 [[AS] 表2别名]
ON 条件
;

查询的结果是仍是一个表。

SELECT语句的执行过程是:

  • 根据WHERE子句的检索条件,从FROM子句指定的基本表或视图中选取满足条件的元组,再按照SELECT子句中指定的列,投影得到结果表。
  • 如果有GROUP子句,则将查询结果按照<列名1>相同的值进行分组。
  • 如果GROUP子句后有HAVING短语,则只输出满足HAVING条件的元组。
  • 如果有ORDER子句,查询结果还要按照<列名2>的值进行排序。

关系代数

例3.21 查询全体学生的学号、姓名和年龄。

1
SELECT SNO, SN, AGE FROM S;

例3.22 查询学生的全部信息。

1
SELECT * FROM S;

用‘ * ’表示S表的全部列名,而不必逐一列出。

例3.23 查询选修了课程的学生号。

SELECT ==DISTINCT== SNO FROM SC;

查询结果中的重复行被去掉

上述查询均为不使用WHERE子句的无条件查询,也称作投影查询。

另外,利用投影查询可控制列名的顺序,并可通过指定别名改变查询结果的列标题的名字。

例3.24 查询全体学生的姓名、学号和年龄。

1
SELECT SNAME NAME, SNO, AGE FROM S;

其中,NAME为SNAME的别名

条件查询

比较大小

多重条件查询

确定范围

例3.28 查询工资在1000至1500之间的教师的教师号、姓名及职称。
1
2
3
4
SELECT TNO,TN,PROF
FROM T
WHERE SAL BETWEEN 1000 AND 1500
;

BETWEEN … AND不是所有的DBMS都支持,为了通用性,改成如下标准形式:

等价于

1
2
3
SELECT TNO,TN,PROF
FROM T
WHERE SAL>=1000 AND SAL<=1500;
例3.29 查询工资不在1000至1500之间的教师的教师号、姓名及职称。

确定集合

利用“IN”操作可以查询属性值属于指定集合的元组。

利用“NOT IN”可以查询指定集合外的元组。

部分匹配查询

上例均属于完全匹配查询,当不知道完全精确的値时,用户还可以使用LIKE或NOT LIKE进行部分匹配查询(也称模糊查询)。

LIKE定义的一般格式为:
1
<属性名> LIKE <字符串常量>

属性名必须为字符型,字符串常量的字符可以包含如下两个特殊符号:

%:表示任意知长度的字符串;

_:表示任意单个字符。 utf-8编码则一个下划线表示汉字,

例3.32 查询所有姓张的教师的教师号和姓名。
1
2
3
SELECT TNO, TN 
FROM T
WHERE TN LIKE '张%';

SQL中的字符串单引号和双引号部分,通常使用单引号

例3.33 查询姓名中第二个汉字是“力”的教师号和姓名。
1
2
3
SELECT TNO, TN 
FROM T
WHERE TN LIKE '__力%';

注:一个汉字占两个字符。

空值查询

常用库函数及统计汇总查询

长度固定的字符串是可以使用MAX和MIN来计算

SUM是求和,COUNT是计数的

不要用任何DBMS的语言去编程数据库自定义函数,然后调用,这样的话无法迁移到其他DBMS

例3.37 求计算机系学生的总数

1
2
SELECT COUNT(SNO) FROM S
WHERE DEPT='计算机';

或者统计有多少条记录:

1
select count(*) from s where WHERE DEPT='计算机';

分组查询

s(sno,sname,sage,dept,sex);

1
2
3
select sex,count(*) as total form s 
group by sex
having count(*)>0;
1
2
3
4
select sex,count(*) as total form s 
where dept='计算机'
group by sex
having count(*)>0;

having之后跟在group by之后

1
2
3
4
select sex,count(*) as total form s 
where dept='计算机'
group by dept,sex
having count(*)>0;

分组之后只会看总体信息,不能看具体信息

例题

1
SC(sno,cno,score);

查询同时选修了c1和c2课程的学生学号?

1
2
3
4
select sno from SC
where cno='c1' or cno='c2'
group by sno
having count(*)=2;

如果都选择这两个课程,那么统计的结果为2,如果只选择了一门,那么统计的结果为1.

分组查询

GROUP BY子句可以将查询结果按属性列或属性列组合在行的方向上进行分组,每组在属性列或属性列组合上具有相同的值。

……

WHERE-GROUP BY- HAVING

当在一个SQL查询中同时使用WHERE子句,GROUP BY 子句和HAVING子句时,其顺序是WHERE-GROUP BY- HAVING。

WHERE与HAVING子句的根本区别

WHERE与HAVING子句的根本区别在于作用对象不同。
WHERE子句作用于基本表或视图,从中选择满足条件的元组;
HAVING子句作用于组,选择满足条件的组,必须用于GROUP BY子句之后,但GROUP BY子句可没有HAVING子句。

查询的排序
当需要对查询结果排序时,应该使用ORDER BY子句
ORDER BY子句必须出现在其他子句之后
排序方式可以指定,DESC为降序,ASC为升序,缺省时为升序

例3.44 查询选修C1 的学生学号和成绩,并按成绩降序排列。
SELECT SNO, SCORE
FROM SC
WHERE CNO=’C1’
ORDER BY SCORE DESC

例3.45 查询选修C2、C3、C4或C5课程的学号、课程号和成绩,查询结果按学号升序排列,学号相同再按成绩降序排列。

1
2
3
4
SELECT SNO,CNO, SCORE 
FROM SC
WHERE CNO IN ('C2' ,'C3', 'C4','C5')
ORDER BY SNO,SCORE DESC ;

例3.46 求选课在三门以上且各门课程均及格的学生的学号及其总成绩,查询结果按总成绩降序列出。

1
2
3
4
5
SELECT SNO,SUM(SCORE) AS TotalScore  FROM SC
WHERE SCORE>=60
GROUP BY SNO
HAVING COUNT(*)>=3
ORDER BY SUM(SCORE) DESC;

执行顺序(重要)

此语句为分组排序,执行过程如下:

1.(FROM)取出整个SC

2.(WHERE)筛选SCORE>=60的元组

3.(GROUP BY)将选出的元组按SNO分组

4.(HAVING)筛选选课三门以上的分组

5.(SELECT)以剩下的组中提取学号和总成绩

6.(ORDER BY)将选取结果排序

数据表连接及连接查询

数据表之间的联系是通过表的字段值来体现的,这种字段称为连接字段。

连接操作的目的就是通过加在连接字段的条件将多个表连接起来,以便从多个表中查询数据。

前面的查询都是针对一个表进行的,当查询同时涉及两个以上的表时,称为连接查询。

表的连接方法有两种:

  • 方法1:表之间满足一定的条件的行进行连接,此时FROM子句中指明进行连接的表名,WHERE子句指明连接的列名及其连接条件。
  • 方法2:利用关键字JOIN进行连接。

查询选修了3门课以上的学生学号

1
2
select sno,sname from S where sno in
(select son form SC grop by sno having count(*)>=3)

能有嵌套查询尽量使用嵌套查询

笛卡尔积

两张表做联查,先做笛卡尔积。

(1,2)X(3,4)=( (1,3),(1,4),(2,3),(2,4) )

内连接

1
2
S(sno,sn)
SC(sno,cno,score)
1
2
3
select s.sno,s.sn,SC.cno,SC.score
from S,SC
where S.sno=SC.sno;

上面的写法成为内连接隐式写法

对应的显示写法为:

1
2
3
select s.sno,s.sn,SC.cno,SC.score
from S inner join SC
on S.sno=SC.sno;

多表内连接显示写法:

1
2
3
4
5
6
select s.sno,s.sn,SC.cno,SC.score
from
(S inner join SC
on S.sno=SC.sno)
inner join C ......
;

连接很费时,能用嵌套查询就用嵌套查询来做

外连接

外连接(outer join)分为如下其中

  • left join
  • right join
  • full join
  • cross join—-就是笛卡尔积

左外连接

保证左边这张表的所有记录都要在结果里面,如果右边没有对应的记录,则使用null填充

1
2
3
select s.sno,s.sn,SC.cno,SC.score
from S left join SC
on S.sno=SC.sno;

右连接

保证右边表中的素所有记录都在结果里,左边没有对应的记录则使用null填充

全连接

左边,右边的记录都保留在结果集里面

INNER JOIN :显示符合条件的记录,此为默认值;
LEFT (OUTER) JOIN:显示符合条件的数据行以及左边表中不符合条件的数据行,此时右边数据行会以NULL来显示,此称为左连接;

RIGHT (OUTER) JOIN:显示符合条件的数据行以及右边表中不符合条件的数据行,此时左边数据行会以NULL来显示,此称为右连接;

FULL (OUTER) JOIN:显示符合条件的数据行以及左边表和右边表中不符合条件的数据行,此时缺乏数据的数据行会以NULL来显示;

CROSS JOIN:会将一个表的每一笔数据和另一表的每笔数据匹配成新的数据行。

当将JOIN 关键词放于FROM子句中时,应有关键词ON与之相对应,以表明连接的条件。

如果非要使用连接的话,可以先使用子查询筛选出要查询的信息作为中间表,尽量缩小数据量之后,在做连接:

image-20210718110107652

自身连接

1
2
3
select a.sno from SC as a,SC
where a.sno=SC.sno
and a.cno='C1' and sc.cno='C2';

ANY不是所有的数据库都支持,不建议使用。

重要

普通子查询和相关子查询

image-20210718111248416

尽量不要使用相关子查询

子查询里面用到父表里面的条件,就是相关子查询

数据库更新

插入数据

insert into

insert的时候不要简写,不要省略字段名,如果数据库迁移的时候,字段的顺序可能会改变。

修改记录

删除记录

下午讲异常机制

先做SQL语句的作业

回顾

后续讲解

SQL语句

JDBC

MVC

数据结构

集合

每周一休息一天

明天下午讲解第2份作业。

关系数据库

信息管理系统必须要有数据支撑。

数据库发展历史

层次结构

实现复杂。

网状结构

实现复杂

关系模型

使用二维表来描述

引例

学生(学号,姓名,性别,…)

s_id s_name s_sex
1 小明
2 小芳

属性列序无关。

image-20210717192055628

建表规则

范式可以理解为约定

第1范式:1NF

在关系表中,所有的属性都是原子属性属性不可再分

只满足第一范式,还不可用

1NF的定义为:符合1NF的关系中的每个属性都不可再分。表1所示的情况,就不符合1NF的要求。

preview

第2范式:2NF

在第1范式的基础之上,所有的非主属性,都是依赖于主属性的

原则:一物一表

二范式就是要有主键,要求其他字段都依赖于主键。

主键

实体完整性约束

实体完整性,由主键决定,录入数据的时候,一定要录入主键

第3范式:3NF

在2NF基础上,任何非主属性不依赖于其它非主属性

在第2范式的基础上,不存在传递依赖

外键

用来实现表和表之间的联系

外键的属性名和原来的属性名可以不一致,但是类型一定要一致。但是实际应用中一定要见名知意,应该把两者写一样。

做到第3范式基本就可以使用了。

参照完整性约束

自定义完整性约束

范式总结:

如何理解关系型数据库的常见设计范式? - 山尽的回答 - 知乎 https://www.zhihu.com/question/24696366/answer/36839826

  • 1NF: 字段是最小的的单元不可再分
  • 2NF:满足1NF,表中的字段必须完全依赖于全部主键而非部分主键 (一般我们都会做到)
  • 3NF:满足2NF,非主键外的所有字段必须互不依赖
  • 4NF:满足3NF,消除表中的多值依赖

如何理解关系型数据库的常见设计范式? - HMKenny的回答 - 知乎 https://www.zhihu.com/question/24696366/answer/72436609

第一范式:属性不可拆分。

第二范式:每个表中的非主属性完全依赖于码。

第三范式:消除非主属性之间的依赖关系,只保留非主属性与码的依赖关系。

BC范式:每个表中只有一个候选键

相关术语

RDBMS

DB

database,就是数据库

DBA

数据库管理员

E-R图

矩形 表示实体

椭圆 表示属性,字段

菱形 表示实体联系,实体关系

线条

关系

只有三种:

  • 一对一
  • 一对多
  • 多对多

E-R图和表的关系

每个实体要建立一张表,一物一表

一对一,不建表,把信息存放

一对多,不建表,一的主键放到多的表里面作为外键

多对多,要建表,把两端主键放到关系表中作为外键,共同组成关系表的主键

常用数据管理系统

SQL Server

informax

Oracle

MySQL

案例4-1 类的继承

一、案例描述

1、考核知识点
编号:00104001
名称:类的继承

2、练习目标

  • 了解类继承的意义和作用
  • 掌握如何实现类的继承

3、需求分析

在程序中,继承描述的是事物之间的所属关系,通过继承可以使多种事物之间形成一种关系体系。
为了让初学者熟悉类的继承,本案例将**演示类的继承并编写测试类==验证子类是否拥有父类的可继承成员==**。

4、设计思路(实现原理)
1)设计两个类Student和Teacher
2)抽取两个类共同的内容(如:吃饭、睡觉)封装到一个类Person中,各自特有的部分保留在各自类中。
3)让学生类继承Person类,老师类也继承Person。
4)编写测试类Example01,测试Student类和Teacher是否继承了Person类的成员。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class HW_01 {

public static void main(String[] args) {
Student student = new Student();
student.eat();
student.sleep();
Teacher teacher = new Teacher();
teacher.eat();
teacher.sleep();
}
}

class Student extends Person {
}

class Teacher extends Person {
}

class Person {
public void eat() {
System.out.println("eat...");
}
public void sleep() {
System.out.println("sleep...");
}
}

运行结果

1
2
3
4
eat...
sleep...
eat...
sleep...

案例4-2 方法的重写

一、案例描述

1、考核知识点
编号:00104002
名称:方法的重写

2、练习目标

  • 了解方法重写的意义和作用
  • 掌握如何进行方法重写

3、需求分析

在继承关系中,子类会自动继承父类中定义的方法,但有时在子类中需要对继承的方法进行一些修改,即对父类的方法进行重写。为了让初学者掌握方法的重写,本案例将编写一个类NewPhone,该类继承Phone类并对Phone类的call()方法进行重写。

4、设计思路(实现原理)

1)定义一个类Phone,编写方法call(),表示打电话功能
2)定义一个Phone的子类NewPhone,重写父类call()方法,让它除了打电话功能外还具有开启语言和关闭语言功能。
3)编写一个测试类Example02,分别调用重写前和重写后的call()方法

二、案例实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class HW_02_Phone {
public static void main(String[] args) {

Phone phone = new Phone();
phone.call();
System.out.println();
phone = new NewPhone();
phone.call();

}
}

class Phone {
/**
* 打电话的方法
*/
public void call() {
System.out.println("Phone 打电话...");
}
}

class NewPhone extends Phone {
@Override
public void call() {
System.out.println("NewPhone 打电话...");
System.out.println("NewPhone 开启语言...");
System.out.println("NewPhone 关闭语言...");
}
}

三、案例总结

1、子类中需要对继承自父类的方法进行一些修改,这时就用到方法重写。

2、在子类中重写的方法需要和父类被重写的方法具有相同的方法名、参数列表以及返回值类型

3、子类重写的方法的访问修饰权限不能小于父类的

4、重写的主要优点是能够定义子类特有的特征。

案例4-3 super访问父类成员变量

一、案例描述

1、考核知识点

编号:00104003
名称:super关键字

2、练习目标

掌握使用super关键字访问父类成员变量

3、需求分析

子类可以继承父类的非私有成员变量,如果在子类中修改了继承自父类的成员变量的值,再想要访问父类的该成员变量时,可以通过super.成员变量来实现。为了让初学者熟悉super关键字的用法,本案例将分别设计Fu类及其子类Zi,并在Zi类的方法中使用super关键字访问Fu类的成员变量。

4、设计思路(实现原理)

  • 1)编写一个Fu类,在类中定义无参构造和一个初始值为20的num成员变量。
  • 2)Zi类继承Fu类,在子类中对num值进行了修改,同时在子类中定义无参构造和一个无返回值的method()方法,method()方法中使用super关键字调用了Fu类的num成员变量。
  • 3)定义测试类Example03。

二、案例实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class HW_03_Fu {
public static void main(String[] args) {
Fu fu = new Fu();
System.out.println(fu.num);
Zi zi = new Zi();
System.out.println(zi.num);
zi.method();
}
}

class Fu {
public int num = 20;

public Fu() {
super();
}
}

class Zi extends Fu {
public int num = 30;

public Zi() {
super();
}

public void method() {
System.out.println(super.num);
}
}

三、案例总结

1、使用super关键字调用父类的成员方法。具体格式如下:

1
super.成员方法([参数1,参数2…])

2、被调用的父类成员变量,必须是非private的。

案例4-5 super访问父类构造方法

一、案例描述

1、考核知识点

编号:00104003
名称:super关键字

2、练习目标

掌握如何在子类构造方法中使用super关键字访问父类构造方法

3、需求分析

在子类的构造方法中一定会调用父类的某个构造方法,如果想指定调用类父类中的哪个构造方法,可以使用super关键字来实现。为了让初学者掌握super关键字的用法,本案例将分别设计Fu类及其子类Zi,在Zi类的构造方法中使用super关键字访问Fu类的构造方法。

4、设计思路(实现原理)

  • 1)编写一个Fu类,在类中定义无参构造
  • 2)Zi类继承Fu类,子类中也定义无参构造方法,在构造方法中使用super关键字调用Fu类的构造方法。
  • 3)定义测试类Example05。

二、案例实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class HW_04_Fu {
public static void main(String[] args) {
Fu1 fu1=new Fu1();
System.out.println();
Zi1 zi1=new Zi1();
}

}
class Fu1 {

public Fu1() {
super();
System.out.println("Fu1....");
}

}
class Zi1 extends Fu1 {

public Zi1() {
super();
System.out.println("Zi1....");
}
}

三、案例总结

1、通过super调用父类构造方法的代码必须位于子类构造方法的第一行,并且只能出现一次。

2、在子类的构造方法中通过super指定调用父类的哪个构造方法,如果没有指定,在实例化子类对象时,会自动调用父类无参的构造方法。

3、被调用的父类构造方法,必须是非private的。

案例4-6 final修饰类

一、案例描述

1、考核知识点

编号:00104004
名称:final关键字

2、练习目标

了解final关键字修饰类的特点
掌握final关键字修饰类的用法

3、需求分析

Java中的类被final关键字修饰后,该类将不可以被继承,也就是不能够派生子类。为了让初学者熟悉final关键字修饰类的用法,本案例将分别设计两个类,一个是使用final关键字修饰的Fu类,另一个是继承Fu类的Zi类,验证final关键字修饰的类是否能被继承。

4、设计思路(实现原理)

  • 1)编写一个final关键字修饰的Fu类,类体可以为空
  • 2)编写Zi类,Zi类继承于Fu类,类体可以为空
  • 3)定义测试类Example06。

二、案例实现

1
2
3
4
5
6
7
8
9
10
public class HW_06_FinalTest {
public static void main(String[] args) {
Fu_2 fu_2 = new Fu_2();
Zi_2 zi_2 = new Zi_2();
}
}
final class Fu_2 {
}
class Zi_2 extends Fu_2 {
}

第9行报错:

1
The type Zi_2 cannot subclass the final class Fu_2

运行报错:

1
2
Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
The type Zi_2 cannot subclass the final class Fu_2

三、案例总结

在Java中,被final关键字修饰的类为最终类,不能被其它类继承。

案例4-7 final修饰方法

一、案例描述

1、考核知识点

编号:00104004
名称:final关键字

2、练习目标

掌握使用final关键字修饰方法

3、需求分析

子类可以继承父类的成员方法,并在必要时对方法进行重写,增加了方法的扩展的同时也打破了方法的封装,如果我们希望父类中的某些方法不能被重写,这时就可以使用final关键字来修饰
为了让初学者掌握使用final关键字修饰方法,本案例将分别设计两个类,一个是Fu类,其中定义了final修饰的show()方法,另一个是继承Fu类的Zi类,在Zi类中对show()方法进行重写。

4、设计思路(实现原理)

  • 1)编写一个Fu类,类中定义final修饰的show()方法。
  • 2)编写Zi类,Zi类继承于Fu类,在Zi类中对show()方法进行重写
  • 3)定义测试类Example07,创建Zi类对象,并调用Zi类show()方法。

二、案例实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class HW_07_FinalMethod {
public static void main(String[] args) {
new Fu_7().show();
new Zi_7().show();
}
}
class Fu_7 {
public final void show() {
System.out.println(Fu_7.class.getTypeName() + ".show...");
}
}
class Zi_7 extends Fu_7 {
public void show() {
System.out.println(Zi_7.class.getTypeName() + ".show...");
}
}

第13行报错:

1
2
3
Multiple markers at this line
- Cannot override the final method from Fu_7
- overrides time20210716.homework.Fu_7.show

运行结果:

1
2
3
time20210716.homework.Fu_7show...
Exception in thread "main" java.lang.VerifyError: class time20210716.homework.Zi_7 overrides final method show.()V
at java.lang.ClassLoader.defineClass1(Native Method)

三、案例总结

当一个类的方法被final关键字修饰后,这个类的子类将不能重写该方法。

案例4-8 final修饰局部变量

一、案例描述

1、考核知识点

编号:00104004
名称:final关键字

2、练习目标

掌握使用final关键字修饰局部变量

3、需求分析

Java中被==final修饰的变量为常量,它只能被赋值一次==,也就是说final修饰的变量一旦被赋值,其值不能改变。为了让初学者掌握使用final关键字修饰局部变量,本案例将在类的方法中定义一个final修饰的局部变量,并试着对该变量重新赋值。

4、设计思路(实现原理)

  • 1)编写一个Example08类,类中定义类一个final修饰的局部变量age,为其赋初始值为18。
  • 2)为age重新赋值为20。

二、案例实现

1
2
3
4
5
final int age=18;
age=20;
final int num;
num=12;
num=18;

第2行报错:

1
The final local variable age cannot be assigned. It must be blank and not using a compound assignment

最后一行报错:

1
The final local variable num may already have been assigned

三、案例总结

final修饰的变量表示常量,一经赋值就不能重新赋值。

案例4-9 final修饰成员变量

一、案例描述

1、考核知识点

编号:00104004
名称:final关键字

2、练习目标

掌握使用final关键字修饰成员变量

3、需求分析

在Java中,final修饰的变量表示常量,一经赋值就不能重新赋值。为了让初学者熟悉final修饰变量的情况,本案例将使用final关键字修饰成员变量,观察其是否能够再次赋值。

4、设计思路(实现原理)

  • 1)编写一个Fu类,父类中定义一个变量X,并用final关键字修饰变量。
  • 2)编写Zi类,Zi类继承于Fu类,在子类中对常量再赋新值。
  • 3)定义测试类Example09,观察运行结果。

二、案例实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class HW_09_FinalField {
public static void main(String[] args) {
// new Fu_9();
new Zi_09();
}
}
class Fu_9 {
public final int X;
public final int Y = 20;
{
Y = 30;
}
public Fu_9() {
X = 10;
}
}
class Zi_09 extends Fu_9 {
public Zi_09() {
X = 50;
}
}

编译器报错

第11行:

1
The final field Fu_9.Y cannot be assigned

第19行报错

1
The final field Fu_9.X cannot be assigned

运行结果:

1
2
3
4
5
Exception in thread "main" java.lang.Error: Unresolved compilation problems: 
The final field Fu_9.Y cannot be assigned
The final field Fu_9.X cannot be assigned
at time20210716.homework.Zi_09.<init>(HW_09_FinalField.java:13)
at time20210716.homework.HW_09_FinalField.main(HW_09_FinalField.java:6)

三、案例总结

在本案例中Zi类中对变量X再次赋值,运行结果报错。这是因为Java中被final修饰的变量为常量,它只能被赋值一次,也就是说final修饰的变量一旦被赋值,其值不能改变。如果再次对该变量进行赋值,则程序会在编译时报错

电脑崩溃,上面的数据丢失了

接口的实现类加上Impl作为后缀

课堂作业

设计一台计算机,(有固定数目的USB接口)

这个计算机支持所有的USB接口设备

可以添加设备

可以移除设备

一键启动所有设备

一键关闭所有设备

DeskPins

下载地址:
https://deskpins.en.softonic.com/

使用方法

切换要置顶的窗口到最前面,

然后单击系统栏上deskPins图标,然后鼠标指针会变成一个图钉。

image-20210716104311337

然后把这个图钉移动到要置顶的窗口上,再单击一次即可

image-20210716104317527

BeiK窗口工具1.0.6

到火绒网站上下载

https://bbs.huorong.cn/forum.php?mod=viewthread&tid=76083&page=1&extra=#pid445706

或者直接点击下载地址:
https://bbs.huorong.cn/forum.php?mod=attachment&aid=NTg0OTN8OWZhMTVlOTB8MTYyNjQwNTE3N3wxMjM1MDl8NzYwODM%3D

使用方法

下载后,解压,然后点击exe文件即可运行,

image-20210716111828727

这个程序会列出当前正在运行的所有程序,在列表中选择一个程序,然后点击下面的置顶按钮即可。

image-20210716112158066

查找路径打开

1
C:\Users\用户名\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup

把上面的用户名换成你的用户名即可打开。

通过运行打开

按下win+r快捷键,然后输入如下命令,即可打开win10的启动项文件夹

1
shell:startup

1、写出一个复数类,给出加、减、乘法、除方法;

image-20210714165256627

image-20210714172231318

加法

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 复数的加法
*
* @param a 复数1
* @param b 复数2
* @return 复数a加上复数b大的值。
*/
public static Complex add(Complex a, Complex b) {
Complex result = new Complex();
result.real = a.real + b.real;
result.imag = a.imag + b.imag;
return result;
}

减法

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 复数减法
*
* @param a 复数1
* @param b 复数2
* @return 复数1减去复数2的结果
*/
public static Complex minus(Complex a, Complex b) {
Complex result = new Complex();
result.real = a.real - b.real;
result.imag = a.imag - b.imag;
return result;
}

乘法

https://www.shuxuele.com/algebra/complex-number-multiply.html

image-20210714172634655

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 求两个复数的乘积
*
* @param first 第一个复数
* @param second 第二个复数
* @return 两个复数的乘积
*/
public static Complex times(Complex first, Complex second) {
Complex result = new Complex();
result.real = first.real * second.real - first.imag * second.imag;
result.imag = first.imag * second.real + first.real * second.imag;
return result;
}

测试1:

1
2
3
4
c1 = new Complex(3, 2);
c2 = new Complex(1, 7);
Complex times = Complex.times(c1, c2);
System.out.println(times);

运行结果:

1
-11.0+23.0i

image-20210714192424595

测试2:

1
2
3
c1 = new Complex(1, 1);
c2 = new Complex(1, 1);
System.out.println(Complex.times(c1, c2));

运行结果:

1
0.0+2.0i

image-20210714192835071

测试3:

1
2
3
c1 = new Complex(3, 4);
c2 = new Complex(0, 1);
System.out.println(Complex.times(c1, c2));

运行结果:

-4.0+3.0i

共轭

共轭是把中间的正负号改变,像这样:

image-20210714193653954

共轭的一般符号是上面放一条横线:

例子:

image-20210714193637751

除法

复数除法需要用到共轭。

技巧是把上面下面都乘以下面共轭

image-20210714194808150

公式

1
(A+Bi)/(C+Di)=(A*C+B*D)/(C*C+D*D) + (B*C-A*D)/(C*C+D*D)i

例子

image-20210714193851139

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* (A+Bi)/(C+Di)=(A*C+B*D)/(C*C+D*D) + (B*C-A*D)/(C*C+D*D)i
* (A+Bi)/(C+Di)=(A*C+B*D)/(C*C+D*D) + (B*C-A*D)/(C*C+D*D)i
*
* @param fenZi 分子
* @param fenMu 分母
* @return 复数fenZi除以复数fenMu的结果
*/
public static Complex divide(Complex fenZi, Complex fenMu) {
Complex result = new Complex();
// A=first.real
// B=first.imag
// C=second.real
// D=second.imag
result.real = (fenZi.real * fenMu.real + fenZi.imag * fenMu.imag)
/ (fenMu.real * fenMu.real + fenMu.imag * fenMu.imag);
result.imag = (fenZi.imag * fenMu.real - fenZi.real * fenMu.imag)
/ (fenMu.real * fenMu.real + fenMu.imag * fenMu.imag);
return result;
}

验证:

1
2
3
4
c1 = new Complex(2, 3);
c2 = new Complex(4, -5);
System.out.println(Complex.divide(c1, c2));
System.out.println((-7 / 41.0) + "+" + (22 / 41.0) + "i");

运行结果:

1
2
-0.17073170731707318+0.5365853658536586i
-0.17073170731707318+0.5365853658536586i

2、写出一个矩形类,给出 面积和周长方法;

Rectangle_02.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class Rectangle_02 {
/**
* 矩形的长度
*/
private double length;
/**
* 矩形的宽度
*/
private double width;

public Rectangle_02() {
// TODO Auto-generated constructor stub
}

public Rectangle_02(double length, double width) {
this.length = length;
this.width = width;
}

/**
* 求矩形的周长
*
* @return 矩形的周长
*/
public double perimeter() {
return 2 * (this.length + this.width);
}

/**
* 求矩形的面积
*
* @return 矩形的面积
*/
public double area() {

return this.length * this.width;
}

@Override
public String toString() {
return "Rectangle [length=" + length + ", width=" + width + "]";
}

}

测试:

1
2
Rectangle_02 r = new Rectangle_02(3, 4);
System.out.println(r + "的周长=" + r.perimeter() + ",面积=" + r.area());

运行结果:

1
Rectangle [length=3.0, width=4.0]的周长=14.0,面积=12.0

3、使用class关键字定义一个表示学生类型的类,类名为Student。

在Student类中定义两个成员变量name和age,分别用来表示姓名和年龄。

其中,name的数据类型为String,变量age的数据类型为int。

在Student类中定义一个表示说话行为的speak()方法,用于输出学生的姓名和年龄。

【练习题】01.类的成员变量:

猜数字游戏:一个类A有一个成员变量v,有一个初值100。定义一个类,对A类的成员变量v进行猜。如果大了则提示大了,小了则提示小了。等于则提示猜测成功。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class HW_02_01_GuessingNumbers {
private static int v = 100;

/**
* 猜测数字和类中定义个数字的v的大小
*
* @param num 整数
* @return 如果num大于类中保存的整数v,则返回1,等于则返回0,小于则返回-1
*/
public static int guessing(int num) {
if (num > HW_02_01_GuessingNumbers.v) {
return 1;
} else if (num == HW_02_01_GuessingNumbers.v) {
return 0;
}
return -1;
}

public static void print(int num) {
if (HW_02_01_GuessingNumbers.guessing(num) > 0) {
System.out.println(num + "大了");
} else if (HW_02_01_GuessingNumbers.guessing(num) == 0) {
System.out.println(num + "猜测成功");
} else {
System.out.println(num + "小了");
}
}
}

【练习题】02.类的成员变量:

请定义一个交通工具(Vehicle)的类,其中有:

属性:速度(speed),体积(size)等等

方法:移动(move()),设置速度(setSpeed(int speed)),加速speedUp(),减速speedDown()等等.

最后在测试类Vehicle中的main()中实例化一个交通工具对象,并通过方法给它初始化speed,size的值,并且通过打印出来。另外,调用加速,减速的方法对速度进行改变。

匀加速直线运动的速度公式:

$$
v_{t}=v_{0}+a t
$$

【练习题】03.类的成员变量与方法、构造方法

在程序中,经常要对时间进行操作,但是并没有时间类型的数据。那么,我们可以自己实现一个时间类,来满足程序中的需要。

定义名为MyTime的类,其中应有

三个整型成员:时(hour),分(minute),秒(second),为了保证数据的安全性,这三个成员变量应声明为私有。

为MyTime类定义构造方法,以方便创建对象时初始化成员变量。 再定义diaplay方法,用于将时间信息打印出来。

为MyTime类添加以下方法:

1
2
3
4
5
6
addSecond(int sec) 
addMinute(int min)
addHour(int hou)
subSecond(int sec)
subMinute(int min)
subHour(int hou)

分别对时、分、秒进行加减运算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/**
* 当前的时间加上{@code h}个小时
*
* @param h 要加上多少个小时
* @return 当前时间加上{@code h}个小时之后的结果
*/
public HW_02_03_MyTime addHour(int h) {
int temp = this.hour + h;
// if (temp > 23)
// 一天只有24个小时,如果超过了24个小时话需要进位
// 由于这个类只保存到小时,所以丢弃进位
this.hour = temp % 24;
return this;
}
/**
* 当前时间加上{@code m}个分钟
*
* @param m 要加上多少分钟,可以超过60分钟
* @return 当前时间加上{@code m}分钟后的时间
*/
public HW_02_03_MyTime addMinute(int m) {
int temp = this.minute + m;
if (temp > 59) {
int hour = temp / 60;
this.minute = temp % 60;
// 向小时进位
addHour(hour);
} else {
// 没有进位的情况下直接在分钟上加即可
this.minute = this.minute + m;
}
return this;
}
/**
* 计算当前时间加上{@code s}秒钟后的时间
*
* @param s 要加上多少秒钟
* @return 当前时间加上{@code s}}秒钟后的时间
*/
public HW_02_03_MyTime addSecond(int s) {
int temp = this.second + s;
// 需要进位的情况
if (temp > 59) {
// 需要进位多少
int minute = temp / 60;
// 求出进位后的秒值
this.second = temp % 60;
// 向分钟进位
addMinute(minute);
} else {
this.second += s;
}
return this;
}
/**
* 当前时间减去{@code h}小时
*
* @param h 需要减去的小时数
* @return 当前时间减去{@code h}小时后的时间
*/
public HW_02_03_MyTime subHour(int h) {
int temp = this.hour - h;
// System.out.println("temp=" + temp);
// 如果减去的小时比当前的小时数要大,则需要借位
if (temp < 0) {
// 小时前面没有更高的位了,直接使用24加上相减的结果即可得到小时数
this.hour = 24 + temp % 24;
} else {
this.hour = temp;
}
return this;
}/**
* 当前时间减去{@code m}分钟
*
* @param m 要减去多少分钟
* @return 当前时间减去{@code m}分钟后的结果
*/
public HW_02_03_MyTime subMinute(int m) {
int temp = this.minute - m;
System.out.println("temp=" + temp);
if (temp < 0) {
// System.out.println("temp="+temp);
// 计算出要减去的小时数
int hour = -temp / 60;
subHour(hour + 1);
// 计算要减去的分钟数
this.minute = (60 + temp % 60) % 60;
} else {
this.minute = temp;
}
return this;
}
/**
* 当前时间减去{@code s}秒钟
*
* @param s 要减去多少秒钟
* @return 当前时间减去{@code s}秒钟后结果
*/
public HW_02_03_MyTime subSecond(int s) {
int temp = this.second - s;
// 相减为负数,则需要借位
if (temp < 0) {
// 计算出要减去的小时数
int minute = -temp / 60;
System.out.println(minute);
subMinute(minute);
// 当temp为0时,括号中的结果为60,此时应该对应0秒,而不是第60秒,
// 所以最后还要mod60
this.second = (60 + temp % 60) % 60;
} else {
this.second = temp;
}
return this;
}

【练习题】04.构造方法

编写Java程序,模拟简单的计算器。

定义名为Number的类,其中有两个整型数据成员n1和n2,应声明为私有。

编写构造方法,赋予n1和n2初始值,

再为该类定义加(addition)、减(subtration)、乘(multiplication)、除(division)等公有成员方法,分别对两个成员变量执行加、减、乘、除的运算。

在main方法中创建Number类的对象,调用各个方法,并显示计算结果。

【练习题】05.构造方法:

编写Java程序,用于显示人的姓名和年龄。

定义一个人类(Person),该类中应该有两个私有属性,姓名(name)和年龄(age)。

定义构造方法,用来初始化数据成员。再定义显示(display)方法,将姓名和年龄打印出来。

在main方法中创建人类的实例,然后将信息显示。

【练习题】06.get方法和set方法

定义一个类,该类有一个私有成员变量,通过构造方法将其进行赋初值,并提供该成员的getXXX()和setXXX()方法

提示:假设有

1
private String name;

则有

1
2
3
4
5
6
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}

【练习题】07.构造方法与重载

为“无名的粉”写一个类:class WuMingFen 要求:

1.有三个属性:

  • 面码:String theMa
  • 粉的分量(两):int quantity
  • 是否带汤:boolean likeSoup

2.写一个构造方法,以便于简化初始化过程,如:

WuMingFen f1 = new WuMingFen("牛肉",3,true);

3.重载构造方法,使得初始化过程可以多样化:

WuMingFen f2 = new WuMingFen("牛肉",2);

4.如何使得下列语句构造出来的粉对象是酸辣面码2两带汤的

WuMingFen f3 = new WuMingFen();

5.写一个普通方法:check(),用于查看粉是否符合要求。即:将对象的三个属性打印在控制台上。

【练习题】08.构造方法的重载:

定义一个名为Vehicles(交通工具)的基类,

  • 该类中应包含String类型的成员属性brand(商标)和color(颜色),

  • 还应包含成员方法run(行驶,在控制台显示“我已经开动了”)和

  • showInfo(显示信息,在控制台显示商标和颜色),

  • 并编写构造方法初始化其成员属性。

编写Car(小汽车)类继承于Vehicles类,

  • 增加int型成员属性seats(座位),

  • 还应增加成员方法showCar(在控制台显示小汽车的信息),

  • 并编写构造方法

编写Truck(卡车)类继承于Vehicles类,

  • 增加float型成员属性load(载重),

  • 还应增加成员方法showTruck(在控制台显示卡车的信息),

  • 并编写构造方法。 在main方法中测试以上各类。

【练习题】09.构造方法与重载

定义一个网络用户类,要处理的信息有

  • 用户ID、
  • 用户密码、
  • email地址。

在建立类的实例时,把以上三个信息都作为构造函数的参数输入,其中

用户ID和用户密码时必须的

缺省的email地址是==用户ID==加上字符串”@gameschool.com”

【练习题】10.构造方法与重载、包

编写Addition类,该类中应包含一组实现两数相加运算的重载方法。 实现加法运算的方法,应接受两个参数(即加数和被加数),方法将两个参数进行加法运算后,返回相加结果。考虑可能针对不同的数据类型进行计算,重载一组方法,包括整型、长整型、浮点型、双精度浮点型、还有字符串。 在main方法中创建Addition类的实例,分别调用重载方法测试其效果。 应将Addition类打入到包中,以自己名字的拼音为包命名。

【练习题】11.构造方法与重载

将上次练习题三中编写的MyTime类打到以自己名字的拼音命名的包中,并为该类重载一组构造方法,以方便使用者能够以多种形式初始化该类的实例。

【练习题】12.构造方法与重载

建立一个汽车类,包括轮胎个数,汽车颜色,车身重量等属性。并通过不同的构造方法创建事例。

至少要求:汽车能够加速,减速,停车。

要求:命名规范,代码体现层次,有友好的操作提示。

【练习题】13.构造方法与重载

创建一个类,为该类定义三个构造函数,分别执行下列操作:

1、传递两个整数值并找出其中较大的一个值

2、传递三个double值并求出其乘积

3、传递两个字符串值并检查其是否相同

4、在main方法中测试构造函数的调用

在Gitee网站上创建仓库 创建私人令牌

省略

下载Typora PicGo

https://typora.io/#windows
https://github.com/Molunerfinn/picgo/releases

PicGo安装gitee插件

image-20210714123545874

Gitee插件配置

image-20210714124204721

Typora调用PicGo自动上传图片

image-20210714124709197

Failed to fetch

如果上面的验证图片上传失败了的话,如下图所示
image-20210714125534665

修改picgo端口号为36677

这是是picgo的服务的端口号和Typora设置的端口号不一样,修改picgo的端口号为36677,让两者一样就行了。
image-20210714125627646
image-20210714125720157

重启picgo

修改端口号之后,还需要重启picgo,让修改生效:
image-20210714125905603

验证成功效果

这样应该就可以正常上传图片了,验证成功效果如下图所示:
image-20210714125923507

粘贴图片到Typora编辑区后,图片会自动上传

参考资料

https://www.jianshu.com/p/4cd14d4ceb1d
https://cloud.tencent.com/developer/article/1801576
https://blog.csdn.net/qq_28988969/article/details/114297273

习题讲解

求最大公约数
int i;
for(i=n;i>0;i–){
if(m%i==0&&n%i==0)
break;
}

回文数

颠倒这个数,如果和原来的数一样的话,那么就是回文数。

1
2
3
4
5
6
7
8
9
10
11
m=12321;
n=m;
s=0;
while(n>0){
s=s*10+n%10
n=n/10;
}

if(s==m){
// 回文数
}

11题 2+22+222+2222+22222

1
2
3
4
5
6
7
8
9
10
11
n=5;
a=2;
s=0;
sn=0;

for(int i=0;;i<n;i++)
{
sn=sn*10+a;
s=s+sn;
}
输出s

面向对象

结构体

1
2
3
4
5
struct book{
double 价格;
char[20] 作者;
char[100] 出版社;
}

定义结构体变量:

1
struct book m;

取别名

1
2
3
4
5
typedef struct book{
double 价格;
char[20] 作者;
char[100] 出版社;
} Book;

类 = 结构体 + 函数
| |
属性 方法

面型对象的特征

  • 封装
  • 继承
  • 多态

继承是为了更好的实现软件复用

在面试的时候,一定要举例子说明。

多态

重载

同一个类里面有多个同名方法,—->见名知意

1
2
int max(int x,int y)
double max(double x,double y)

重写

在继承的情况下。

高内聚 松耦合

模块之内要高内聚,模块之间要松耦合。

类之内要高内聚,类之间要松耦合。

添加新功能,应该封装实现

对别人的功能上添加新功能,不要重写,要包装别人的类,如果有问题那么就是原来的类的问题,责任不在自己

类的声明和类体

java命令规则

类的名字不能是java中的关键字,要符合标识符规定

标识符规定

标识符可以由字母,下划线,数字或美元符号组成,并且第一个字符不能是数字
a$是合法的(日常使用中应避免使用美元符号作为标识符)

类的成员属性

属性写在类的前面,方法写在后面

类的成员方法

方法的名字必须符合标识符规范,
首字母小写,其他单词首字母要大写。

构造方法

构造方法的名字必须和类名相同,并且不能写返回值类型。
如果写了返回值类型,那么该方法就不在是构造方法,而是普通方法。

new出来的空间都在堆,其他的都在栈空间。

this指针

编译器会在调用每个对象的时候,写到方法的形参里面。
picgo-plugin-gitee-uploader

就近原则

优先使用最近的同名变量

编程题 求两点之间的距离

new是申请空间,构造函数是初始化

编程题 写一个圆Circle类

给定圆心和半径

求周长,面积,位置的关系,相离,相切,相交。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class Circle {
// 圆心
private Point center;
// 半径
private double radius;

public Circle() {
// TODO Auto-generated constructor stub
}

public Circle(Point center, double radius) {
super();
this.center = center;
this.radius = radius;
}

/**
* 求圆的周长
*
* @return 圆的周长
*/
public double perimeter() {
return 2 * Math.PI * radius;
}

/**
* 求圆的面积
*
* @return 圆的面积
*/
public double area() {
return Math.PI * radius * radius;
}

/**
* 判断两个圆的位置关系
*
* @param other 另一个圆
* @return 如果两个圆相离,则返回1;
* 如果两个圆相交,则返回0;
* 如果两个圆相切则返回-1。
*/
public int position(Circle other) {
// 如果两个圆的圆心的距离大于半径
double radiusSum = this.radius + other.radius;
if (this.center.distance(other.center) > radiusSum)
return 1;
else if (Math.abs(this.center.distance(other.center) - radiusSum) < 1e-6) {
return 0;
}
return -1;
}
}

写方法时,参数越少越好。

如果一个类之中没有写构造函数,编译器会给这个类写上一个默认的构造函数。

如果你已经在类之中写了构造函数,则编译器不会提供默认的构造函数。

空的类系统依然会分配内存,通常是一个字节

编程题 复数的加减法

a+bi

复数的运算法则

https://baike.baidu.com/item/%E5%A4%8D%E6%95%B0/254365#5

image-20210714150942916

常量

final定义的变量就是常量。

1
public final in MAX=99;

常量不能被重新赋值。

构造函数中给常量赋值

常量可以先声明,然后在构造函数中赋值。

1
2
3
4
5
6
public class Demo{
public final int MAX;
public Demo(){
this.MAX=2;
}
}

注意,只能在构造函数中赋值,不能在其他方法中给常量赋值。

static

类中的常量应该使用static修饰,不然的话每个对象都存储一遍常量会浪费内存。

JDK7以上版本,静态域存储于定义类型的Class对象中

static定义的成员,应该使用类名来调用,不要用this来调用

static定义的方法,编译器不会给static方法增加this形参,所以这个static方法不能去访问对象。

如果确实要在static方法中访问对象,可以自己在形参列表中传入该对象的引用。

静态方法只能访问静态的成员变量和成员方法

编程题 定义一个类,要求该类知道创建了多少该类的对象

使用static定义一个计数器,然后在构造函数中给计数器加一

单例模式

  • 先要隐藏构造函数,类的构造函数私有化。
  • 提供一个静态的公共方法给别人获取对象。
  • 在类中,要记下已经创建好的对象。

代码块

如果在类之中的成员变量,在定义时赋值,在代码块中赋值,在构造器中赋值。

执行顺序为:

直接赋值,代码块,构造函数

如果有多个代码块,多个代码块之间按定义大的顺序执行。

静态代码块只会执行一次

静态代码块在.class文件加载到内存中就运行了,因为类只会加载一次,所以静态代码块也就只会执行一次

1、编写两个函数,分别求最大公约数(greatest common divisor)和最小公倍数(least common multiple)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class GCD_LCM {
public static void main(String[] args) {
int a = 12, b = 10;

a = 4;
b = 2;
int gcd = getGCD(a, b);
System.out.println(a + "和" + b + "的最大公约数为:" + gcd);
System.out.println(a + "和" + b + "的最小公倍数为:" + lcm(a, b, gcd));
}

/**
* 辗转相除法求最大公约数入口
*
* @param a 一个整数
* @param b 另一个整数
* @return 整数a和整数b的最大公约数
*/
private static int getGCD(int a, int b) {
if (a > b) {
return gcd(a, b);
} else {
return gcd(b, a);
}
}

/**
* 辗转相除法求最大公约数
*
* @param big 大的整数
* @param small 小的整数
* @return big和small这整数的最大公约数
*/
private static int gcd(int big, int small) {
int remainder = big % small;
if (remainder == 0) {
return small;
} else {
return gcd(small, remainder);
}
}

/**
* 求最小公倍数
*
* @param a 整数
* @param b 另一个整数
* @param gcd a和b的最大公约数
* @return a和b的最小公倍数
*/
private static int lcm(int a, int b, int gcd) {
return (a * b) / gcd;
}
}

2、编写一个判断素数(prime number)的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class PrimeNumber {
public static void main(String[] args) {
for (int i = 2; i < 1000; i++) {
if (isPrime_sqrt(i)) {
if (i != 2) {
System.out.print(",");
}
System.out.print(i);
}
}
System.out.println();
}

/**
* 判断是否是质数
* 优化版本1
*
* @param n 一个数
* @return 如果num是质数的话,返回true,否则返回false.
*/
private static boolean isPrime_sqrt(int n) {
// n只需要被小于等于sqrt(n)的数整除即可判断是否为质数
int sqrtN = (int) Math.sqrt(n);
for (int i = 2; i <= sqrtN; i++) {
// 如果能被小于它的数整除的话,那就不是质数
if (n % i == 0)
return false;
}
return true;
}
}

3、编写函数,使得给定的一个二维数组(3*3)转置(transposition)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public class MatrixTranspose {
public static void main(String[] args) {
int[][] a = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
printMatrix(a);

// int[][] b = transposeByCopy(a);
// System.out.println();
// printMatrix(b);

System.out.println();
transpose(a);
printMatrix(a);
}
/**
* 矩阵转置(方阵)
* @param a 保存正方形矩阵的二维数组
*/
private static void transpose(int[][] a) {
for (int i = 0, temp; i < a.length; i++) {
for (int j = 0; j < a[i].length; j++) {
// 交换主对角(j==i)线上下方的元素位置
if (j < i) {
temp = a[i][j];
a[i][j] = a[j][i];
a[j][i] = temp;
}
}
}
}
/**
* 矩阵转置
* @param a 保存矩阵的二维数组
* @return 保存转置后的矩阵的二维数组
*/
private static int[][] transposeByCopy(int[][] a) {
int[][] b = new int[a.length][a[0].length];
for (int i = 0; i < a.length; i++) {
for (int j = 0; j < a[i].length; j++) {
b[j][i] = a[i][j];
}
}
return b;
}

/**
* 打印矩阵
*
* @param a 保存矩阵的二维数组
*/
private static void printMatrix(int[][] a) {
for (int i = 0; i < a.length; i++) {
System.out.print("│");
for (int j = 0; j < a[i].length; j++) {
if (j > 0)
System.out.print(",");
System.out.print(a[i][j]);
}
System.out.println("│");
}
}
}

4、编写一个函数,使得输入的一个字符串反序存放。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class StringStoredInReverseOrder {
public static void main(String[] args) {
// char[] ch=new char[100];
String str = "hello world!!";
char[] ch = str.toCharArray();
printChars(ch);
inverse(ch);
printChars(ch);
}
/**
* 反序存放字符串
* @param ch 保存字符串的字符数组。
*/
private static void inverse(char[] ch) {
char temp;
for (int i = 0; i < ch.length / 2; i++) {
temp = ch[i];
ch[i] = ch[ch.length - 1 - i];
ch[ch.length - 1 - i] = temp;
}
}

private static void printChars(char[] ch) {
for (int i = 0; i < ch.length; i++) {
System.out.print(ch[i]);
}
System.out.println();
}
}

5、编写一个函数,连接(connect)两个字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class StringConcatenation {
public static void main(String[] args) {
String str1 = "hello";
String str2 = "world";
char[] ch1 = str1.toCharArray();
char[] ch2 = str2.toCharArray();

char[] ch3 = connect(ch1, ch2);
printChars(ch3);
}

/**
* 连接两个字符串
*
* @param ch1 保存第一个字符串的字符数组
* @param ch2 保存另一个字符串的字符数组
* @return ch1和ch2串联起来形成的字符数组
*/
private static char[] connect(char[] ch1, char[] ch2) {
char[] ch3 = new char[ch1.length + ch2.length];
for (int i = 0; i < ch1.length; i++) {
ch3[i] = ch1[i];
}
for (int i = 0; i < ch2.length; i++) {
ch3[ch1.length + i - 1] = ch2[i];
}
return ch3;
}

private static void printChars(char[] ch) {
for (int i = 0; i < ch.length; i++) {
System.out.print(ch[i]);
}
System.out.println();
}
}

6、编写一个函数,将一个字符串中的元音字母(vowel:a、e、i、o、u)复制到另一个字符串中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class TakeOutVowels {
public static void main(String[] args) {
// 待输入的字符串
String str = "helloWorld+aeiou";
// 元音字典
char[] vowels = { 'a', 'e', 'i', 'o', 'u' };
char[] strCh = str.toCharArray();

char[] vowelsBuffer = vowel(vowels, strCh);
printChars(vowelsBuffer);
}

private static char[] vowel(char[] vowels, char[] strCh) {
int k = 0;
char[] vowelsBuffer = new char[200];
for (int i = 0; i < strCh.length; i++) {
for (int j = 0; j < vowels.length; j++) {
if (strCh[i] == vowels[j]) {
// System.out.print("(" + i + "," + strCh[i] + ")");
if (k < 200)
vowelsBuffer[k++] = strCh[i];
else {
System.out.println("内存已满");
k = 0;
}
}
}
}
return vowelsBuffer;
}

private static void printChars(char[] ch) {
for (int i = 0; i < ch.length; i++) {
System.out.print(ch[i]);
}
System.out.println();
}
}

7、编写一个函数,将字符串中最长的单词输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class FindLongestWord {

public static void main(String[] args) {
String[] strs = { "i", "like", "chinese", "NiceToMeetYouToo", "helloworld", "NiceToMeetYou" };
int longestIndex = findLongestWordIndex(strs);
System.out.println("最长的单词为" + strs[longestIndex]);
}

private static int findLongestWordIndex(String[] strs) {
int[] strLengths = new int[strs.length];
// 最长的单词的下标
int longestWordIndex = 0;
for (int i = 0; i < strLengths.length; i++) {
// 如果当前的单词比最长的单词还要长
if (strs[i].length() > strs[longestWordIndex].length()) {
// 那么认为当前这个单词是最长的
longestWordIndex = i;
}
}
return longestWordIndex;
}
}

8、编写一个编码函数

根据码长Key,把输入字符串按如下要求进行编码,并返回;
若是字母a-z 或是 A-Z, 0-9
假定key=1,
则源码中
字母 b->c , z->a
字母 2-> 3 9->0

Key=-1, 则 a->z, 0->9
函数原型为: char[] encoding(String s, int key)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
public class Encoding {

public static void main(String[] args) {
String str = "abcdefg";
str = "abcdefghijklmnopqrstuvwxyz";
str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
str = "0123456789";
str = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

int key = 2;
key = 3;


char[] coded = shiftCoding(str, key);

System.out.println(String.valueOf(coded));



// printAsciiCodeSequence('a', 26, "'", "'", ",");
// printAsciiCodeSequence('A', 26, "'", "'", ",");
// printAsciiCodeSequence('0', 10, ",");
// printAsciiCodeSequence('0', 10, "'", "'", ",");

// printAsciiCodeSequence('a', 26);
// printAsciiCodeSequence('A', 26);
// printAsciiCodeSequence('0', 10);
}

/**
* 移位编码,讲一个单词在字母表中后移n位或者前移n位实现编码
*
* @param str 需要编码的字符串(只支持大小写加数字)
* @param key 需要移动的位数
*/
private static char[] shiftCoding(String str, int key) {
char[] lowerCaseLetters = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
char[] upperCaseLetters = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q',
'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
char[] numbers = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
char[] strs = str.toCharArray();
char[] coded = new char[strs.length];

for (int i = 0; i < strs.length; i++) {
if (strs[i] >= 'a' && strs[i] <= 'z') {
if (strs[i] + key > 'z') {
// System.out.print((char) (strs[i] + key - 26));
coded[i] = (char) (strs[i] + key - 26);
} else {
// System.out.print((char) (strs[i] + key));
coded[i] = (char) (strs[i] + key);
}
}
if (strs[i] >= 'A' && strs[i] <= 'Z') {
if (strs[i] + key > 'Z') {
// System.out.print((char) (strs[i] + key - 26));
coded[i] = (char) (strs[i] + key - 26);
} else {
// System.out.print((char) (strs[i] + key));
coded[i] = (char) (strs[i] + key);
}
}
if (strs[i] >= '0' && strs[i] <= '9') {
if (strs[i] + key > '9') {
// System.out.print((char) (strs[i] + key - 10));
coded[i] = (char) (strs[i] + key - 10);
} else {
// System.out.print((char) (strs[i] + key));
coded[i] = (char) (strs[i] + key);
}
}
}
return coded;
}

/**
* 打印连续的ASCII码
*
* @param firstASCII 刚开始的ASCII码
* @param times 要排在firstASCII之后多少位的ASCII
* @param head 打印在 ASCII码 前面的字符串
* @param tail 打印在ASCII码 之后的字符串
* @param delimiter 分隔符
*/
private static void printAsciiCodeSequence(char firstASCII, int times, String head, String tail, String delimiter) {
for (int i = 0; i < times; i++) {
if (i > 0)
System.out.print(delimiter);
System.out.print(head + (char) (firstASCII + i) + tail);
}
System.out.println();
}
/**
* 连续打印ASCII字符序列
* @param firstASCII 要打印的ASCII字符的开始字符
* @param times 要打印的ASCII字符序列的长度
*/
private static void printAsciiCodeSequence(char firstASCII, int times) {
printAsciiCodeSequence(firstASCII, times, "", "", "");
}

private static void printAsciiCodeSequence(char firstASCII, int times, String delimiter) {
printAsciiCodeSequence(firstASCII, times, "", "", delimiter);
}
}

9、Int atoi(String s) // 把数字字符串转成数值 “367”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class StringtoInt {
public static void main(String[] args) {
String str = "367";
int num = stringToInt(str);
System.out.println(num);
}

private static int stringToInt(String str) {
char[] ch = str.toCharArray();
int num = 0;
for (int i = 0, value; i < ch.length; i++) {
// 取出每一位的值
value = (int) (ch[i] - '0');
// 计算每一位对应的值
for (int j = 0; j < ch.length - 1 - i; j++) {
value = value * 10;
}
// System.out.println(value);
// 和前面的数字对应的值相加
num += value;
}
return num;
}
}

10、 Double atod(String s)

String类型的小数转double

11、输入年月,输出该月的月历

(1) 输入年月日,求出当年的第几天(一个函数)
(2) 输入年月日,输入星期几( 以1990-1-1星期一为基准)
(3) 输入年月,打印日历

12、 Char[] change(String s) 实现字符串单词倒置

例如:

1
2
Hello**world
olleH**dlrow