


Java 开发环境搭建


变量和数据类型和运算符
1.注释
为了方便程序的阅读,Java 语言允许程序员在程序中写上一些说明性的文字,用来提高程序的可读性,这些文字性的说明就称为注释。注释不会出现在字节码文件中,即 Java 编译器编译时会跳过注释语句。在 Java 中根据注释的功能不同,主要分为单行注释、多行注释和文档注释。
单行注释: 单行注释使用“//”开头,“//”后面的单行内容均为注释
2.标识符
3.变量
变量的声明:
double salary;
long earthPopulation;
int age;
//声明变量的同时初始化
int age = 18;
double e = 2.718281828;
4.变量的分类和作用域
从整体上可将变量划分为局部变量、成员变量(也称为实例变量)和静态变量。

public class TestConstants {
public static void main(String[ ] args) {
final double PI = 3.14;
// PI = 3.15; //编译错误,不能再被赋值!
double r = 4;
double area = PI * r * r;
double circle = 2 * PI * r;
System.out.println("area = " + area);
System.out.println("circle = " + circle);
}
}
5.基本数据类型

long a = 55555555; //编译成功,在 int 表示的范围内(21 亿内)。
long b = 55555555555;//不加 L 编译错误,已经超过 int 表示的范围。
报错:The literal 55555555555 of type int is out of range,所以我们需要修改代码 为:
long b = 55555555555L;

//float 类型常量的写法及变量的声明
float f = 3.14F;//float 类型赋值时需要添加后缀 F/f
double d1= 3.14;
double d2 = 3.14D;
/*浮点类型 float,double 的数据不适合在不容许舍入误差的金融计算领域。如果需要进 行不产生舍入误差的精确数字计算,需要使用 BigDecimal 类。*/
float f = 0.1f;
double d = 1.0/10;
System.out.println(f==d);//结果为 false
float d1 = 423432423f;
float d2 = d1+1;
if(d1==d2){
System.out.println("d1==d2");//输出结果为 d1==d2
}else{
System.out.println("d1!=d2");
}
/*
运行以上两个示例,发现示例1的结果是“false”,而示例2的输出结果是“d1==d2”。 这是因为由于字长有限,
浮点数能够精确表示的数是有限的,因而也是离散的。 浮点数一 般都存在舍入误差,很多数字无法精确表示
(例如0.1),其结果只能是接近, 但不等于。二 进制浮点数不能精确的表示0.1、0.01、0.001这样10的负次
幂。并不是所有的小数都能可 以精确的用二进制浮点数表示。 java.math 包下面的两个有用的类:
BigInteger 和 BigDecimal,这两个类可以处理任 意长度的数值。BigInteger 实现了任意精度的整数运
算。BigDecimal 实现了任意精度的浮点运算。
*/
public class Main {
public static void main(String[ ] args) {
BigDecimal bd = BigDecimal.valueOf(1.0);
bd = bd.subtract(BigDecimal.valueOf(0.1));
bd = bd.subtract(BigDecimal.valueOf(0.1));
bd = bd.subtract(BigDecimal.valueOf(0.1));
bd = bd.subtract(BigDecimal.valueOf(0.1));
bd = bd.subtract(BigDecimal.valueOf(0.1));
System.out.println(bd);//0.5 System.out.println(1.0 - 0.1 - 0.1 - 0.1 - 0.1 -
0.1);//0.5000000000000001
}
}
6.运算符(operator)


短路与和短路或采用短路的方式。从左到右计算,如果只通过运算符左边的操作数就能够确定该逻辑表达式的值,则不会继续计算运算符右边的操作数,提高效率。

条件运算符:
7.数据类型的转换
强制类型转换,又被称为造型(cast),用于强制的转换一个数值的类型。在有可能丢失信息的情况下进行的转换是通过造型来完成的,但可能造成精度降低或溢出。
(type)var 运算符“()”中的 type 表示将值 var 想要转换成的目标数据类型。
double x = 3.94;
int nx = (int)x; //值为 3
char c = 'a';
int d = c+1;
System.out.println(nx); //值为3
System.out.println(d); //值为98
System.out.println((char)d); //值为b
int x = 300;
byte bx = (byte)x; //值为 44
int money = 1000000000; //10亿
int years = 20; //返回的total是负数,超过了int的范围
int total = money*years;
System.out.println("total="+total); //返回的total仍然是负数。默认是int,因此结果会转成int值,
再转成long。但是已经发 生//了数据丢失
long total1 = money*years;
System.out.println("total1="+total1); //返回的total2正确:先将一个因子变成long,整个表达式发生
提升。全部用long来计算。
long total2 = money*((long)years);
System.out.println("total2="+total2);
简单的键盘输入和输出
//使用 Scanner 获取键盘输入
import java.util.Scanner;
public class Welcome2 {
public static void main(String[ ] args) {
// 将输入的一行付给 string1 String string1 = scanner.nextLine();
Scanner scanner = new Scanner(System.in);
// 将输入单词到第一个空白符为止的字符串付给 string2
String string2 = scanner.next();
// 将输入的数字赋值给变量
int a = scanner.nextInt();
System.out.println("-----录入的信息如下-------");
System.out.println(string1);
System.out.println(string2);
System.out.println(a * 10);
}
}
控制语句
1 条件判断结构
2 switch 语句
switch (表达式) {
case 值 1:
语句序列 1;
[break];
case 值 2:
语句序列 2;
[break];
… … … … …
[default:
默认语句;
]
}
3 循环结构(while)
while (布尔表达式) {
循环体;
}
//while 循环结构:求 1 到 100 之间的累加和
public class Test7 {
public static void main(String[ ] args) {
int i = 0;
int sum = 0; // 1+2+3+…+100=?
while (i <= 100) {
sum += i;//相当于 sum = sum+i;
i++;
}
System.out.println("Sum= " + sum);
}
}
do {
循环体;
} while(布尔表达式) ;
for (初始表达式; 布尔表达式; 迭代因子) {
循环体;
}
for 循环语句是支持迭代的一种通用结构,是最有效、最灵活的循环结构。
for 循环在 第一次反复之前要进行初始化,即执行初始表达式;
随后,对布尔表达式进行判定,若判定 结果为 true,则执行循环体,否则,终止循环;
最后在每一次反复的时候,进行某种形式 的“步进”,即执行迭代因子。
初始化部分设置循环变量的初值
条件判断部分为任意布尔表达式
迭代因子控制循环变量的增减
for 循环在执行条件判定后,先执行的循环体部分,再执行步进。
public class Test13 {
public static void main(String[] args) {
for(int i = 1; i < 10; i++) {
System.out.println(i+"、");
}
System.out.println(i);//编译错误,无法访问在 for 循环中定义的变量 i
}
}
4 嵌套循环
//使用嵌套循环实现九九乘法表
public class Test15 {
public static void main(String args[ ]) {
for (int i = 1; i < 10; i++) {
// i 是一个乘数
for (int j = 1; j <= i; j++) {
// j 是另一个乘数
System.out.print(j + "*" + i + "=" + (i * j < 10 ? (" " + i * j) : i * j) +
" "); }
System.out.println();
}
}
}
5 break 语句和 continue 语句
//把 100~150 之间不能被 3 整除的数输出,并且每行输出 5 个
public class Test17 {
public static void main(String[ ] args) {
int count = 0;//定义计数器
for (int i = 100; i < 150; i++) {
//如果是 3 的倍数,则跳过本次循环,继续进行下一次循环
if (i % 3 == 0){
continue;
}
//否则(不是 3 的倍数),输出该数
System.out.print(i + "、");
count++;//没输出一个数,计数器加 1
//根据计数器判断每行是否已经输出了 5 个数
if (count % 5 == 0) {
System.out.println();
}
}
}
}
6 方法
方法用于定义该类或该类的实例的行为特征和功能实现。 方法是类和对象行为特征的抽象。方法很类似于面向过程中的函数。面向过程中,函数是最基本单位,整个程序由一个个函数调用组成。面向对象中,整个程序的基本单位是类,方法是从属于类和对象的。
7 递归结构
public class Test22 {
public static void main(String[ ] args) {
long d1 = System.currentTimeMillis();
System.out.printf("%d 阶乘的结果:%s%n", 10, factorial(10));
long d2 = System.currentTimeMillis();
System.out.printf("递归费时:%s%n", d2-d1); //耗时:32ms
}
/** 求阶乘的方法*/
static long factorial(int n){
if(n==1){
//递归头
return 1;
}else{
//递归体
return n*factorial(n-1);//n! = n * (n-1)!
}
}
}
1. 类的定义

public class Person {
String name;
int age;
public void show(){
System.out.println("姓名:"+name+",年龄:"+age);
}
}
public class TestPerson {
public static void main(String[ ] args) {
// 创建p1对象
Person p1 = new Person();
p1.age = 24; p1.name = "张三"; p1.show();
// 创建p2对象
Person p2 = new Person();
p2.age = 35; p2.name = "李四"; p2.show();
}
}
public class User {
int id; //id
String name; //账户名
String pwd; //密码
public User(int id, String name) {
this.id = id; this.name = name;
}
public static void main(String[ ] args) {
User u1 = new User(100, "高小七");
User u3 = u1;
System.out.println(u1.name);
u3.name="张三";
System.out.println(u1.name);
}
}
//输出高小七,张三
-
垃圾回收原理和算法
-
内存管理Java 的内存管理很大程度指的就是:堆中对象的管理 ,其中包括对象空间的分配和释 放。对象空间的分配: 使用 new 关键字创建对象即可对象空间的释放: 将对象赋值 null 即可。垃圾回收器将负责回收所有”不可达”对象 的内存空间
-
垃圾回收过程任何一种垃圾回收算法一般要做两件基本事情:1. 发现无用的对象2. 回收无用对象占用的内存空间。垃圾回收机制保证可以将“无用的对象”进行回收。无用的对象指的就是没有任何变量 引用该对象。Java 的垃圾回收器通过相关算法发现无用对象,并进行清除和整理。
- 垃圾回收算法
1. 引用计数法堆中的每个对象都对应一个引用计数器,当有引用指向这个对象时,引用计数器加1,而当指向该对象的引用失效时(引用变为 null),引用计数器减 1,最后如果该对象的引用计算器的值为 0 时,则 Java 垃圾回收器会认为该对象是无用对象并对其进行回收。优点是算法简单,缺点是“循环引用的无用对象”无法识别。
public class Student { String name; Student friend; public static void main(String[ ] args) { Student s1 = new Student(); Student s2 = new Student(); s1.friend = s2; s2.friend = s1; s1 = null; s2 = null; } }
s1 和 s2 互相引用对方,导致他们引用计数不为 0,但是实际已经无用,但无法被识别。2. 引用可达法(根搜索算法)程序把所有的引用关系看作一张图 ,从一个节点 GC ROOT 开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点。
-
-
通用的分代垃圾回收机制
分代垃圾回收机制,是基于这样一个事实:不同的对象的生命周期是不一样的。因此,不同生命周期的对象可以采取不同的回收算法,以便提高回收效率。我们将对象分为三种状态:年轻代、年老代、持久代。同时,将处于不同状态的对象放到堆中不同的区域。 JVM将堆内存划分为 Eden、Survivor 和 Tenured/Old 空间。
1. 年轻代所有新生成的对象首先都是放在 Eden 区。 年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象,对应的是 Minor GC,每次 Minor GC 会清理年轻代的内存,算法采用效率较高的复制算法,频繁的操作,但是会浪费内存空间。当“年轻代”区域存放满对象后,就将对象存放到年老代区域。2. 年老代在年轻代中经历了 N(默认 15)次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。年老代对象越来越多,我们就需要启动 Major GC 和 Full GC(全量回收),来一次大扫除,全面清理年轻代区域和年老代区域。3. 永久代用于存放静态文件,如 Java 类、方法等。持久代对垃圾回收没有显著影响。JDK7以前就是“方法区”的一种实现。JDK8 以后已经没有“永久代”了,使用 metaspace元数据空间和堆替代。Minor GC:用于清理年轻代区域。Eden 区满了就会触发一次 Minor GC。清理无用对象,将有用对象复制到“Survivor1”、“Survivor2”区中。Major GC:用于清理老年代区域。Full GC:用于清理年轻代、年老代区域。 成本较高,会对系统性能产生影响。 - JVM 调优和 Full GC
在对 JVM 调优的过程中,很大一部分工作就是对于 Full GC 的调节。有如下原因可能导致 Full GC:1. 年老代(Tenured)被写满2. 持久代(Perm)被写满3. System.gc()被显式调用4. 上一次 GC 之后 Heap 的各域分配策略动态变化
-
开发中容易造成内存泄露的操作
创建大量无用对象比如,我们在需要大量拼接字符串时,使用了 String 而不是 StringBuilder。
静态集合类的使用像 HashMap、Vector、List 等的使用最容易出现内存泄露,这些静态变量的生命周期和应用程序一致,所有的对象 Object 也不能被释放。各种连接对象(IO 流对象、数据库连接对象、网络连接对象)未关闭IO 流对象、数据库连接对象、网络连接对象等连接对象属于物理连接,和硬盘或者网络连接,不使用的时候一定要关闭。监听器的使用不当释放对象时,没有删除相应的监听器 -
其它要点
1.程序员无权调用垃圾回收器2. 程序员可以调用 System.gc(),该方法只是通知 JVM,并不是运行垃圾回收器。尽量少用,会申请启动 Full GC,成本高,影响系统性能。3. finalize 方法,是 Java 提供给程序员用来释放对象或资源的方法,但是尽量少用
this、static 关键字
包机制(package、import)
import java.sql.Date;
import java.util.*;//导入该包下所有的类。会降低编译速度,但不会降低运行速度。
public class Test{
public static void main(String[ ] args) {
//这里指的是java.sql.Date Date now;
//java.util.Date因为和java.sql.Date类同名,需要完整路径
java.util.Date now2 = new java.util.Date();
System.out.println(now2);
//java.util包的非同名类不需要完整路径
Scanner input = new Scanner(System.in);
}
}
package cn.sxt;
//以下两种静态导入的方式二选一即可
import static java.lang.Math.*;
//导入Math类的所有静态属性
import static java.lang.Math.PI;
//导入Math类的PI属性
public class Test2{
public static void main(String [ ] args){
System.out.println(PI);
System.out.println(random());
}
}
继承
public class Test{
public static void main(String[ ] args) {
Student s = new Student("微微",172,"Java");
s.person.rest(); //s.rest();
s.study();
}
}
class Person {
String name;
int height;
public void rest(){
System.out.println("休息一会!");
}
}
class Student /*extends Person*/ {
Person person = new Person();
String major; //专业
public Student(String name,int height,String major) {
//天然拥有父类的属性
this.person.name = name; //this.name = name;
this.person.height = height; //this.height = height;
this.person.rest();
this.major = major;
}
}
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
//根据如上源码得知,默认会返回“类名+@+16 进制的 hashcode”。
//在打印输出或者用字符串连接对象时,会自动调用该对象的 toString()方法。
封装
未进行封装的代码演示
class Person {
String name;
int age;
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
public class Test {
public static void main(String[ ] args) {
Person p = new Person();
p.name = "小红";
p.age = -45;//年龄可以通过这种方式随意赋值,没有任何限制
System.out.println(p);
}
}
//我们都知道,年龄不可能是负数,也不可能超过 130 岁,但是如果没有使用封装的话,便可以给年龄赋值成
任意的整数,这显然不符合我们的正常逻辑思维。
多态
class Animal {
public void shout() {
System.out.println("叫了一声!");
}
}
class Dog extends Animal {
public void shout() {
System.out.println("旺旺旺!");
}
public void seeDoor() {
System.out.println("看门中....");
}
}
class Cat extends Animal {
public void shout() {
System.out.println("喵喵喵喵!");
}
}
public class TestPolym {
public static void main(String[ ] args) {
Animal a1 = new Cat(); // 向上可以自动转型
//传的具体是哪一个类就调用哪一个类的方法。大大提高了程序的可扩展性。
animalCry(a1);
Animal a2 = new Dog();
animalCry(a2);//a2 为编译类型,Dog 对象才是运行时类型。
/*编写程序时,如果想调用运行时类型的方法,只能进行强制类型转换。
* 否则通不过编译器的检查。*/
Dog dog = (Dog)a2;//向下需要强制类型转换
dog.seeDoor();
}
// 有了多态,只需要让增加的这个类继承 Animal 类就可以了。
static void animalCry(Animal a) {
a.shout();
}
/* 如果没有多态,我们这里需要写很多重载的方法。
* 每增加一种动物,就需要重载一种动物的喊叫方法。非常麻烦。
static void animalCry(Dog d) {
d.shout();
}
static void animalCry(Cat c) {
c.shout();
}
*/
}