注释
含义:程序中的注释,用来说明某段代码的作用,或者说明某个类的用途、某个属性的含义、某个方法的功能·,方法参数和返回值数据类型、意义等。简单来说就是对类中的代码进行说明解释
作用:注释可以增加代码的可读性,当然程序员最讨厌的就是写注释和看没有注释的代码,所以注释可以让自己或者他人更好的解析自己编写的代码,同时有利于后期对系统的维护和升级。
- 单行注释
- 多行注释
- 文档注释
注释代码不会被代码编译,也不会对程序运行产生影响,当然在·单行注释中也有例外,在单行注释会进行说明。
单行注释
public class Test{
// 主方法 程序的入口 mian
public static void main (String[] args){
System.out.print("hello");
}
}
其中// 后面就代表的是注释部分,并不会对源代码的的编译运行有任何影响
特殊情况
public class Test{
//主方法 程序的入口 mian
public static void main (String[] args){
// 定义一个int类型的变量 a 并赋值2
int a = 2;
// /u000d a = 3;
System.out.println("a:"+a);
}
}
在一个程序中,按照注释不会被编译的说法,a的值应该为2,但事实上a输出的结果为3,这是因为 /u000d类似于换行,这就使得 a=3
这一行代码被换到了下一行,而// 是单行注释,不在该行的代码会将不会被注释,这就使得a的重新赋了一个3的值
多行注释
/*
类名:Test
作用:多行注释测试类
日期:2020-08-03
作者:凉拌小番茄
*/
public class Test{
public static void main (String[] args){
System.out.print("hello");
}
}
/* */表示多行注释,多行注释可以单行注释,也可以多行注释,但不会出现单行注释的情况。但值得注意的是多行注释不能嵌套多行注释,不然会编译报错,就是不会在多行注释当中在进行多行注释,但可以进行单行注释//
文档注释
文档注释:/** */,可以生成API文档(说明文档,对Java程序进行解释说明)
在生成API文档的时候,如果可以得到耕读的详细细信息 可以使用javadoc标记,常用的标记有:
- @author:作者
- @version:版本
- @since:jdk版本
- @deprecated:不推荐使用的方法、过时的方法。
- @param:方法的参数类型。
- @return:方法的返回类型。
- @see:用于指定参考的内容。
- @exception:抛出的异常。
- @throws:抛出的异常,和exception同义
- @see:关联其他类
package com.briup.day03;
/**
* 文档注释测试类,利用文档注释,结合javadoc命令,可以生成API说明文档
* @author 凉拌小番茄
* @version 1.0
* @since JDK1.8
* @see com.briup.day03.Comment03
*/
public class Comment04 {
/**
* 类中的属性,表示问好语句的模式
* 0 - hi xxx
* 1 - hello xxx
* 默认是模式为 1
*/
public int mode = 1;
/**
* 返回向指定人问好的语句
* @param name 用户姓名,向其问好
* @return 问候的语句
* @exception RuntimeException 运行时,mode值不是0或者1,会出现异常
*/
public String sayHello(String name)throws RuntimeException{
if (mode == 0){
return "hi! "+name;
}
else if(mode == 1){
return "hello! "+name;
}
else{
throw new RuntimeException("error mode");
}
}
/**
* @deprecated 测试方式,已经废弃
*/
public void test(){}
}
生成对应doc文件的命令
javadoc -d api -author -version src/*.java
javadoc -d api -sourcepath src -subpackages com -author -version
-sourcepath 指定源代码存放的位置
-subpackages 指定要递归查找的包的名字
符号
分号
在Java中,一句代码的结束是用英文分号来表示。
需要使用分号的代码语句有:
- 包的声明语句
- 类的引入语句
- 属性的声明语句
- 方法中要执行的代码
当然也有不需要加分号的代码
- 类的声明
- 方法的声明
- 代码块的声明
简单来说就是花括号{}后面不需要跟分号来代表语句结束。
空白
在代码中,可以使用空格,tab、换行(\n),回车(\r),并且对代码没有影响
,总体就是为了让代码更加整齐,美观,方便阅读等,写代码要优雅
注意,不可以使用这些空白来分割单词或关键字
标识符
在Java中,给类,方法,变量起的名字就是标识符。
命名规则
- 标识符可以由字母、数字、下划线_ 、美元符号$组成
- 标识符开头不能是数字
- 标识符中的字符大小写敏感
- 标识符的长度没有限制
- 标示符不能使用java中的关键字或保留字
当然标识符满足这些规则就可以在代码中正常使用,但还有一些推荐规则,还是那句话,写代码要优雅
推荐规则
- 尽量使用有意义的名字,尽量做到见名知义。
例如,int numOfStudnet = 10; String userName = “tom”;
- 包名 最好是域名倒过来,要求所有的字母小写
package com.briup.test;
- 类和接口,首字母大写,如果是俩个单词,第二个单词的首字母大写(驼峰命名规则)
例如,public class Account{} ,public interface AccountBase{}
- 方法和变量,首字母小写,如果是俩个单词,第二个单词的首字母大写(驼峰命名规则)
例如,public void getStudentName(){} , int personNum = 1;
- 常量,全部字母大写,如果是俩个单词,使用下划线分隔
例如,public static final int MAX_NUM = 10;
在计算机中只要加法运算,没有减法的算,我们所知的减法其实是加上它的相反数(加负数)
原码
最高位表示符号位,其中1代表负号,0代表正号,其他位存放该数的二进制的绝对值。
以1010为例
最高位(第一位)的1代表的就是负号,真正的值是后三位,换算为十进制为2,所以1010表示的是十进制的-2.
原码的计算,正数之间的运算不会出错,但是正数与负数相加,就引起错误
0001 + 1001 = 1010 (1 +(-1)= -2)
而这就需要反码
反码
反码:
正数的反码还是等于原码
负数的反码就是他的原码除符号位外,按位取反。
相加的结果是一个反码,不能够直接转十进制需要将反码转成原码才能转成10进制
反码解决了相反数相加不为零的问题
0001 + 1110 = 1111 ( 1 +(-1)= - 0)
但反码还是存在一定的问题,就是两个负数相加时,结果也会出错
1110(-1)+ 1100(-3)= 1010(-5)
这时候就需要补码
补码
正数补码与原码一样
负数补码是反码末位 +1
计算机里面存储的就是补码, 运算也是采用补码运算
注意,java中,正数补码按位取反(包括符号位)在加1,就是对应的负数,负数的补码按位取反再加1,就是对应的正数
基本类型
- byte
- short
- int
- long
- float
- double
- char
- boolean
这八种数据类型表示了Java中最基本的数据的结构
boolean
布尔类型占1个字节(8位),它的的值,必须是true或者false,在JVM中会转换为1(true)或者0(false)
char
char类型占2个字节(16个比特位),用来表示字符,String表示字符串,时类类型。一个String由0~n个char组成,字符char使用单引号表示,字符串String使用双引号表示。
//单个字符都可以正常显示,单个字符就是不论是什么,只要在编码表中都对应一个编码值,那么就是单个字符
char a = 'a';
char a = '我';
char a = '1';
//除此之外,还可以用其他方法表示
//使用Unicode编码值表示字符a
char c = '\u0061';
//0x开头的数字位十六进制,使用十六进制表示字符a(0x开头代表16进制)
char c = 0x0061;
//使用十进制数字表示字符a
char c = 97;
//0开头的数字为八进制,使用八进制表示字符a(0开头代表八禁止)
char c = 0141;
//注意:一个中文汉字就是一个字符
char c = '中';
除了这些还有一个特殊的字符,例如
常用转义字符 | 效果 | 使用 |
\n | 换行符,将光标定位到下一行的开头 | ‘\n’ |
\r | 回车,把光标移动到行首(和环境有关) | ‘\r’ |
\t | 垂直制表符,将光标移到下一个制表符的位置 | ‘\t’ |
\\ | 反斜杠字符 | ‘\’ |
\’ | 单引号字符 | ‘’’ |
\" | 双引号字符 | ‘"’ |
整型
- byte 8位、 1字节 范围:负2的7次方~2的7次方减1
- short 16位、2字节 范围:负2的15次方~2的15次方减1
- int 32位、4字节 范围:负2的31次方~2的31次方减1
- long 64位、8字节 范围:负2的63次方~2的63次方减1
有符号整数把二进制数的首位作为符号数,当首位是0时,对应十进制的正整数,当首位是1时,对应十进制的负整数。
0000 0000 0000 0001 : 1
1000 000 0000 0001: -1
//类型的转换,在没有类型声明的情况之下,默认是int的类型
/*
byte<short<int<long
其中大类型不能向小类型之间转换,不然出现精度丢失的错误,但小类型可以向大类型直接转换
*/
byte a = 3;
//在这里就会报错,因为1默认的类型是int类型,int的类之间转换为byte类型
byte b = a+1;
//这里编译不会报错,因为在前面加了一个(byte)强换类型,但运行结果可能会与预期结果不符,这是因为a是变量
byte b =(byte)a+1;
//这里系统不会报错,因为对于这样的定值,编译的时候会自动运算,只要在byte的取值范围之内都不会报错
byte c = 1+1;
//在这里,编译也会报错,因为byte是1个字节,取值范围-127~128,超过这个范围,系统就会报错
byet d = 200;
//在这里编译不会报错,但运行结果会与预期结果不符,这是因为超过了byte的范围,超出范围的部分会被舍弃,剩余部分也会经过计算机补码运算规则进行计算。
byte e = (byte) 200;
Java中的=是赋值的意思,将左边的值赋予右边
四种整型类型的声明:
byte a1 = 1; (内存中占8位) 1字节
short a2 = 1; (内存中占16位)2字节
int a3 = 1; (内存中占32位)4字节
long a4 = 1L; (内存中占64位)8字节
使用long类型数据的时候,后面要加大写L或者小写l,建议加上大写的L,因为小写的l和数字1很像似。
浮点型
float和double都是java中的浮点型,浮点型可以用来表示小数,它们的二进制表示方式和整型不同 35 * 10^(-1),因为计算机中没有小数的概念,在计算机中默认的就是科学计数法的形式
float是32位, 1bit(符号位) 8bits(指数位) 23bits(尾数位)
double是64位 1bit(符号位) 11bits(指数位) 52bits(尾数位)
float和double的精度是由尾数的位数来决定的。浮点数在内存中是按科学计数法来存储的.
数值 = 尾数 × 底数 ^ 指数,(附加正负号)
- float的精度为6 ~ 7位左右有效数字 (2 ^ 23 = 8388608)
- double的精度为15 ~ 16位左右有效数字 (2^52 = 4503599627370496)
俩种浮点型数据的声明:
//后面加f或者F
float f = 10.5f;
//后面加d或者D,默认double
double d = 10.5d;
浮点型的二进制形式:可以使用API提供的方法获取浮点数的二进制形式
float f = 10.5f;
//将f的值转化为int类型
int b = Float.floatToIntBits(f);
System.out.println(Integer.toBinaryString(b));
浮点型的默认类型是double,对于给出一个字面值是10.5的数据,在没有指明这个数据是什么具体的类型的情况下,那么java中默认认为是double类型。
例如,
//编译通过
//字面值1.5默认类型是double
double d = 1.5;
double d = 1.5d;
//编译报错
//字面值1.5默认类型是double
//double和float的精确度不一样
float f = 1.5;
//f2编译通过,因为字面值1的类型是int
//f3编译报错,因为字面值1.5的类型是double
float f1 = 10.5f;
float f2 = f1+1;
double f3 = f1+1.5 +(-0.5);
浮点型的精度丢失:
例如,
System.out.println(1.0-0.66);
//输出结果: 0.33999999999999997
Java中的浮点数类型float和double不能够进行精确运算,虽然大多数情况下是运行是正常的,但是偶尔会出现如上所示的问题。
这个问题其实不是java语言的bug,而是因为计算机存储数据是二进制的,而浮点数实际上只是个近似值,所以从二进制转化为十进制浮点数时,精度容易丢失,导致精度下降。
要保证运行结果的精度,可以使用BigDecimal类:
//add方法 +
//subtract方法 -
//multiply方法 *
//divide方法 /
BigDecimal d1 = BigDecimal.valueOf(1.0);
BigDecimal d2 = BigDecimal.valueOf(0.66);
double result = d1.subtract(d2).doubleValue();
System.out.println(result);
//输出结果:0.34
BigDecimal是java.math包中的类,使用时需要import导入
作业:
1.请编写一个类,并生成相对应API文档,需要在文档上体现作者和版本信息
package com.briup.test;
/**
@author 凉拌小番茄
@version 1.0
@since JDK 1.8
*/
public class Test{
/**
* main方法,程序入口,写法固定
* @param args 程序入口参数,如果需要,运行时可以给main方法进行传参
*/
public static void main (String[] args){
/**
定义一个数组 arr
*/
int arr[]={2,4,1,3,7};
/**
外层循环 遍历次数
*/
for(int i=0;i<arr.length;i++){
/**
内部循环 遍历数组指定元素
*/
for(int j=0;j<arr.length-i-1;j++){
/**
判断语句 两个元素比较 找出最小值
*/
if(arr[j]>arr[j+1]){
int temp=arr[j+1];
arr[j+1]=arr[j];
arr[j]=temp;
}
}
}
/**
增强for循环 遍历排序好的数组 将数值打印出来
*/
for(int s:arr){
System.out.print(s+" ");
}
}
}
javadoc -d api -author -version Test.java -encoding utf-8
2.以下代码有没有问题,如果有问题,请指出来
long a = 1L;
int b = a + 1;
从long转换到int可能会有损失
3.以下代码是否报错,请阐述原因,怎么修改,修改后结果是多少,为什么?
byte b = 130;
byte的取值范围在-128~127之间,130超过这个范围,编译报错
修改方法:
1、将变量b定义为short 或者int 这些范围更广泛的类型 b=130
2、强制转换为byte类型 byte b =(byte) 130;此时b最终输出的值为-126
4.java中基本数据类型是什么,每一个在内存中占多少比特位,多少字节
类型 比特位 字节
boolean 8 1
char 16 2
byte 8 1
short 16 2
int 32 4
long 64 8
float 32 4
double 64 8
5.变量的作用是什么
变量的作用就是用来接收、保存、传递、操作对应的数据的。
附加题
构建两个线程A,B,让两个线程交替输出 A1,B2,A3,B4 …
A线程输出 A开头的数据
B线程输出 B开头的数据
package com.crm.systemset.controller;
/**
* 创建两个线程和一个标志位
* 如果标志位为true,A执行,执行完后并且将标志位改为false
* 如果标志位为false B执行,并且完后并且将标志位改为true
* 同时标志位要被两个线程共享
*/
public class A {
/**
* 用来 ++
*/
int a = 0;
/**
* 标志位
*/
boolean flag = true;
/**
* 锁对象,两个线程使用同一个锁对象
*/
Object object = new Object();
public void print(){
// 加锁
synchronized (object) {
try {
// 获取当前线程的名字
String name = Thread.currentThread().getName();
// 如果 flag == false 同时正在执行的线程为 A,那么就释放锁对象,让该线程进入等待
if (!flag && name.equals("A") ) {
object.wait();
// 如果 flag == true 同时正在执行的线程为 B 那么就释放锁对象,让该线程进入等待
} else if(flag && name.equals("B")) {
object.wait();
}
// 打印线程名 + 数值变化
System.out.println(name + (++a));
// 为了更加显示效果,将当前线程休眠1s 不释放锁
Thread.sleep(1000);
// 执行完成后,将flag 变成相反的值
flag = !flag;
// 唤醒等待的线程
object.notifyAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void start() {
// 创建线程,Thread()里面采用拉姆达表达式,并且将线程的名字设置为A
Thread t1 = new Thread(() -> {
while(true) {
print();
}
},"A");
// 启动线程
t1.start();
// 创建线程,Thread()里面采用拉姆达表达式,并且将线程的名字设置为B
Thread t2 = new Thread(() -> {
while(true) {
print();
}
},"B");
// 启动线程
t2.start();
}
public static void main(String[] args) {
// 创建对象
A a = new A();
// 调用方法
a.start();
}
}