编译运行命令
javac xxx.java //编译生成.class文件
java xxx //执行.class文件
javap xxx.class //将.class文件反编译成java代码
内存机制
java内存可分为:stack
(栈)、heap
(堆)和method area
(方法区)
栈
栈描述的是方法的内存模型,每个方法被调用都会创建一个栈帧(存局部变量、操作数和方法出口灯),属于线程私有,不能实现线程间共享,由系统分配,速度快,是一个连续的内存空间
堆
存储创建好的对象,JVM只有一个堆,被所有线程共享,其是一个不连续的空间,分配灵活,但速度慢
方法区
用于存储类、常量相关信息,即永远是不变或者唯一的内容(代码、类、静态变量/方法、字符串常量等),本质也是堆,JVM只有一个方法区,被所有线程共享
分布机制
局部变量在栈里;
实例化对象分配的内存在堆里;
静态变量在数据区里;
代码在代码区里
变量
局部变量
在方法里、代码块({}
)内部定义的变量,只能在其所在的代码块内能使用,从属于方法,并且使用前需要初始化
成员变量
即实例变量,在方法外、类的内部定义的变量,从属于对象,使用前可以不用初始化,此时按下面标准进行转换:
int 0
double 0.0
char '\u0000'
boolean false
静态变量
即类变量,通过static
修饰符定义,作用于从属于类,生命周期伴随类始终
各种变量如下所示:
public class test {
int a; // 成员变量,从属于对象,默认是0
static int s; //静态变量,从属于类
public static void main(String[] args) {
boolean b = true; // 局部变量,从属于方法,作用域是整个main函数
if(b){
int c = 1; // 局部变量,作用域在if{}内部
System.out.println(c+"");
}
}
}
常量
即无法被修改的数据,由final
修饰符定义
基本数据类型
整型
常用int
(4字节),还有byte
(1字节)、short
(2字节),超过2^32大小则需要为long
型(8字节),long型后面需要加l
或L
,举例:
int a = 1;
long b = 999999999999999L; //long型,不加L或l会报错
注:
八进制用0
开头,十六进制用0x
/0X
开头,二进制用0b
/0B
开头,举例:
010 #八进制
0xa1 #十六进制
0b10 #二进制
注:
如果long
还不够存储数据,可以用java.math
下的BigInteger
实现任意长度数据处理
浮点型
默认是double
型,如果要定义float
型需要在后面加个f
,还有科学计数法表示方式,用e
/E
来修饰,举例:
double a = 1.23; //默认double
float b = 1.24f; //float型,如果没有f会报错
double a = 1.03242e-5; //科学计数法
注:
float
和double
都无法做到任意精度的运算,如果需要的话可以用java.math
下的BigDecimal
来实现任意精度运算
char型
表示'\u0000'
到'\uFFFF'
之间的字符
boolean型
只有true
和false
(全小写),在内存中占1位而不是1个字节,因此不能用1/0来代替
运算语句
取模运算
取模运算的余数符号和左边的操作数相同,举例:
-7%3=-1
7%-3=1
&/&&
前者两个条件都会判断,后者只要判断一个为false
则直接返回false
|/||
前者两个条件都会判断,后者只要判断一个为true则直接返回true
^
异或,不同为true,相同为false
~
补码取反,对补码的0和1互换
<</>>
左移/右移位运算,即乘2/除2运算
x?y:z
若x为true则值为y,否则为z
==/equals
判断是否相等,但前者是比较地址是否相等,所以对于实例化的类,因为都是新开辟内存空间,所以不论内容是否一样都一定是不等的;要想让其相等,就需要使用equals,从而仅判断内容是否相等,但是一般我们都是对像String这种对象进行判断是否equals,而那里面的equals方法是重写过的,所以到自己建的类时如果要判断两个类内容是否相等时需要我们自己去重写equals方法来制定是否内容相同的判断标准,比如我们新建一个类People,如果name相同的两个人就算相等,语句如下:
public class aaa {
public static void main(String[] args){
String a = "dawson";
People p1 = new People(a);
People p2 = new People(a);
System.out.println(""+ p1.equals(p2));
}
}
class People{
String name;
People(String a){
this.name = a;
}
}
结果会发现输出是:false,原因也很显然,是两个不同的对象,当然不等,但是现在我们要当名字相同时就相等,因此可以在类里重写方法equals:
public boolean equals(Object obj){
if(obj instanceof People){
People p = (People) obj;
if (this.name.equals(p.name)){
return true;
}else
return false;
}
return false;
}
结果再次判断会发现输出为:true
常用语句
赋值语句
int i, j = 1; //结果j=1,i没有定义值
判断语句
if-else
语句,还有if-else if-else
,举例:
int a = 2;
if(a == 0){
System.out.println(0);
}else if(a == 1){
System.out.println(1);
}else{
System.out.println(2);
}
注:
要注意的是if语句里面定义的变量只在当前判断语句里面能用,离开当前判断语句后里面的变量即不可访问,所以在判断语句没有用{}
,只会影响到下一句的情况下,下一句如果是定义语句,则毫无意义,因此会报错:此处不允许使用变量声明。这个在循环语句中也是同理
循环语句
常用的循环语句有while
和for
,在java1.5以后多了一种遍历数组的循环方式,举例:
int[] a = {1,2,3,4,5};
for(int i: a)
System.out.println(""+i);
可以理解成python的:
a = [1,2,3,4,5]
for i in a:
print(i)
带标签的break/continue
break
/continue
本来是对最近的循环起作用,但是可以给循环设置标签,然后通过break
/continue
跳转到指定标签,举例:
x:for(int i=0; i<10; i++){ //给循环加上x标签
for(int j=0; j<10; j++){
if(j == 1){
continue x; //对x标签起作用的continue
}
System.out.println(i);
}
}
输入语句
需要实例化一个Scanner
对象(在java.util
下),举例:
Scanner scanner = new Scanner(System.in); //实例化一个输入流对象
String name = scanner.nextLine(); //获取输入的一行数据
System.out.println(name);
int age = scanner.nextInt(); //再获取输入的一行int型的数据
System.out.println(age);
其也可以传入一个文件对象,将会读取该文件的内容
退出语句
使用System
下的exit()
即可,举例:
System.exit(0);
语句计时
使用System下的currentTimeMillis()
可以获取当前时间戳,然后相减即可,举例:
long startTime = System.currentTimeMillis();
for(int i=0; i<100000; i++);
long endTime = System.currentTimeMillis();
System.out.println(endTime - startTime);
类型转换
通用
基本数据类型强转只需要:(数据类型)变量
,即可,比如:
double d1 = 1.223;
int i1 = (int)d1;
int i2 = 1;
float f1 = (float)i2;
char c1 = 'a';
char c2 = 'b';
byte b1 = 'c';
byte b2 = (byte)(b1*2-c1-c2);
boolean
不能和任何类型之间相互转换
String
转int,举例:
String s = "1";
int c = Integer.parseInt(s);
各种类型转String,使用String.valueOf()
,举例:
int i = 1;
System.out.print(String.valueOf(i)); //1
int
转String有两种方法:
(1)Integer.toString(int)
,举例:
int a = 1;
String b = Integer.toString(a);
(2)"" + int
,举例:
int a = 1;
String b = "" + a; //第二种方法适合大部分类型转成字符串
混合类型计算
1.byte
/short
/char
进行计算时都会先转成int型再进行计算,举例:
byte b1 = 1;
btye b2 = 2;
byte b3 = (byte)(b1 + b2); //因为计算时会自动转成int型,所以计算完成后还需要强转会byte型
常用关键字
new
实例化对象,赋值符左边的是编译类型,右边的是运行类型,举例:
Object o = new java.util.Date();
那么编译类型就是Object
,而运行类型就是Date()
。此时如果给实例化对象o
调用Date()
下的方法则会报错,因为编译当中会检查编译类型是否含有该方法,如果有则编译通过,否则不通过(即只能调用编译类型里有的方法)。但因为对象o的真实类型是Date,所以可以通过强转成Date类,从而调用其下面的方法,即先执行下面语句:
java.util.Date d = (java.util.Date)o;
this/super
指代当前对象/父类,因为是针对对象的,所以不能用于静态方法当中
extends
继承类,继承接口用implements
,类不支持多继承,接口支持多继承,类没有继承时,默认继承java.lang.Object
类。要注意的是继承时会继承父类的所有属性和方法,包括private权限的,但是无法访问,如果需要访问,可以通过定义方法来设置和返回
static
设置静态变量/方法(也称为类变量/方法),使其能够保持内容的同步,类似类里的全局变量,在计数器等地方应用较多。生命周期和类相同,在整个应用程序执行期间都有效。
main函数都带有static修饰符,所以可以知道其是静态上下文,因此其只能够引用静态变量(即带static修饰符变量),比如下面的类:
class bbb{
int a = 1;
}
如果在main函数中调用bbb.a
则会报错,此时必须得实例化后才能够调用对象里的,而如果类改成:
class bbb{
static int a = 1;
}
那么就可以直接调用了(调用方法为class.xxx
,如果在当前类下则可以直接xxx
),比如:
bbb.a = 100;
结果就会变成100
静态方法也一样,如果定义的不是静态方法,即使在同一个类里也需要实例化以后才能调用,但是如果是静态方法,则不需要对象,可以直接调用,举例:
public class Test {
public static void main(String[] args) {
int a = 2;
int b = 3;
int i = new Test().add(a, b); //实例化后调用
int j = mul(a, b); //直接调用静态方法
System.out.println("i:" + i);
System.out.println("j:" + j);
}
int add(int x, int y){ //非静态方法
return x + y;
}
static int mul(int x, int y){ //静态方法
return x * y;
}
}
static
还可以修饰静态初始化块,该语句块会在类初始化的时候执行,举例:
public class test {
static String a = "one";
String b = "three";
static {
System.out.println(a); //第一个输出
System.out.println("two"); // 第二个输出
// System.out.println(b); //会报错,因为类初始化只会初始化一些常量和静态变量等,而b是在对象里才存在的
System.out.println(1); // 第三个输出,程序入口是test类的main函数,所以test类是第一个被初始化的
}
public static void main(String[] args) {
System.out.println(2); // 第四个输出
MMM x = new MMM();
}
}
class MMM {
static {
System.out.println(3); // 第五个输出,实例化对象前会先初始化类,所以先执行静态语句块
}
MMM() {
System.out.println(4); // 第六个输出,类初始化完成后执行构造方法
}
}
final
final定义的变量不能被改变;final定义的方法不能被重写;final定义的类不能被继承
instanceof
用法:Object instanceof class
,当对象是右边类或子类所实例化的对象时返回true,举例:
class XYZ {
}
class ABC extends XYZ{
}
class CDE {
}
class aaa {
public static void main(String[] args) {
XYZ xyz = new XYZ();
ABC abc = new ABC();
CDE cde = new CDE();
System.out.println(xyz instanceof XYZ); //true
System.out.println(abc instanceof XYZ); //true
System.out.println(xyz instanceof ABC); //false
System.out.println((Object)cde instanceof ABC); //false,因为cde的类和ABC没有继承之类的关系,所以需要转换成Object才能通过编译
}
}
package/import
设置/导入包,如果出现几个包里有重名类的情况,可以用下面的方式:
java.util.Date date = new java.util.Date();
//java.sql.Date;和java.util.Date;会冲突
还可以通过static
导入包下的所有静态属性,举例:
import static java.lang.Math.PI; //导入math包下的PI
访问控制
private
只有自己类内部能访问,即使实例化了也无法访问和设置,但是可以通过定义方法来设置和返回,举例:
public class test {
public static void main(String[] args) {
A a = new A();
a.setI(100); //a.i = 100;会报错,因此调用该方法则没问题
System.out.println(a.getI());
}
}
class A {
private int i = 1; //无法直接访问
public void setI(int i) { //定义设置方法
this.i = i;
}
public int getI() { //定义返回方法
return this.i;
}
}
要注意的是对于boolean
的数据,get
方法要改成is
方法,举例:
private boolean good ;
public boolean isGood() {
return good;
}
default
不设置,默认就是default,在上面的基础上同一个包下也能访问(即同一个文件夹下)
protected
在上面的基础上不同包的子类也能访问
public
任意地方都能访问
注:
上面的都是设置了访问权,但并没有赋予修改权;对于类只能设置default
和public
两种
重载
overload
,方法名相同,形参类型或个数或顺序不同即可构成重载,举例:
int add(int x, int y) {
return x + y;
}
String add(String x, int y) { //形参类型不同
return x + y;
}
String add(int x, String y){ //形参顺序不同
return x + y;
}
double add(double x, double y, double z){ //形参个数不同
return x + y + z;
}
重写
override
,将从父类继承的方法重写,要求重写的方法名、形参都和父类的一样(直接copy就行),并且返回值和声明异常类型小于等于父类(比如返回父类、子类都是可以的,但是返回Object类不行),重写的对象的访问权限大于等于父类的(比如父类用了public,那么子类就必须也是public)
继承的构造方法
构造方法用于对象的初始化,方法名和类名必须相同,子类构造过程必须调用其父类的构造方法,调用方法:子类构造方法的第一行里通过super(参数)
来调用父类的对应构造方法,如果没有写调用父类构造方法的话,默认调用父类的无参构造方法(相当于直接super()
)
注:
也可以用this(参数)
来调用自己类的其他构造方法,然后在这个构造方法里用super(参数)
调用父类的构造方法,举例:
public class test {
int x, y, z;
public static void main(String[] args) {
//
}
test(int x, int y) {
this.x = x;
this.y = y;
}
test(int x, int y, int z) {
// this.x = x;
// this.y = y;
this(x, y); //上面两句可以替换成这句,但要注意构造器的调用必须放在第一句
this.z = z;
}
}
动态绑定和多态
多态针对的是方法,在代码运行时,会根据对象来执行对应的方法,需要满足三个条件:有继承;有重写;父类引用指向子类对象
举例1:
public class test {
public static void main(String[] args) {
Beef b = new Beef();
Mutton m = new Mutton();
putFood(b); //输出牛肉
putFood(m); //输出羊肉
}
public static void putFood(Food f){ //父类引用指向子类对象
f.putName();
}
}
class Food {
public void putName(){
System.out.println("食物");
}
}
class Beef extends Food { //继承+重写
public void putName(){
System.out.println("牛肉");
}
}
class Mutton extends Food {
public void putName(){
System.out.println("羊肉");
}
}
举例2:
class Food {
String name;
public Food(String name){
this.name = name;
}
public void output(){ //默认输出nothing
System.out.println("nothing");
}
}
class Beef extends Food{
int price;
public Beef(String name, int price){
super(name);
this.price = price;
}
public void output(){ //重写方法,如果是牛肉就输出价格
System.out.println("beef's price:" + this.price);
}
}
class Mutton extends Food{
int weight;
public Mutton(String name, int weight){
super(name);
this.weight = weight;
}
public void output(){ //重写方法,如果是羊肉就输出体重
System.out.println("mutton's weight:" + this.weight);
}
}
class People{
String name;
Food food; //根据传入的食物
public People(String name, Food food){
this.name = name;
this.food = food; //动态选择绑定的食物类
}
public void printOutput(){
this.food.output();
}
}
class aaa {
public static void main(String[] args) {
Beef beef = new Beef("beef", 1);
Mutton mutton = new Mutton("mutton", 10);
People people1 = new People("p1", beef); //传入Beef类
People people2 = new People("p2", mutton); //传入Mutton类
people1.printOutput(); //beef's price:1
people2.printOutput(); //mutton's weight:10
//可以看出根据传入的类对象来执行对应的方法
}
}
通过该方式,可以大大增强扩展性,只需要继承一个类,之后就可以根据不同的子类来对应执行不同的方法
抽象类/抽象方法
都用abstract
来修饰,当一个类里有抽象方法时,则这个类必须定义为抽象类,抽象类是不能实例化的,其是用来被继承的;抽象方法只定义不写具体实现,然后在子类里必须重写(否则报错),或者可以在子类的再次定义成抽象方法(那么此时该子类也变成了抽象类,然后由该子类的子类去实现)
接口
java不支持多继承,而接口能够解决该问题。其是一种特殊的抽象类,是一种所有方法都是public的抽象方法(接口里不需要用abstract
来修饰,默认就是abstract
+public
),所有属性都是public static final
这样不能够被改变的常量类型(默认就是该类型),即抽象方法和常量值的集合,通过interface
来定义,举例:
public interface TimeCount{
int startTime = 0; //public static final类型
public void start(); //抽象方法
public void end(); //抽象方法
}
接口可以被多继承,继承使用implements
,如果要同时继承类和接口,就先用extends
继承类,然后再继承接口
注:
接口之间可以继承,类之间可以继承,接口可以被类继承,但最后一定是类来实现接口
内部类
即在类的内部再创建类,分为成员内部类、匿名内部类和局部内部类
成员内部类
又分为静态和非静态内部类(是否有static
修饰)
非静态内部类
此时内部类可以直接访问外部类的成员(但反过来不行),并且内部类不能有静态方法、静态属性和静态初始化块,访问外部类成员时,通过外部类.this.xxx
访问,实例化时必须先实例化外部类然后在实例化内部类,举例:
public class Test {
public static void main(String[] args) {
AAA.BBB b = new AAA().new BBB(); // 先new外部类,再new内部类
b.getA();
}
}
class AAA {
public int a = 1;
class BBB {
public int a = 2;
void getA() {
int a = 3;
System.out.println("外部类变量:" + AAA.this.a); // 调用外部类变量,1
System.out.println("内部类变量:" + this.a); //2
System.out.println("局部变量:" + a); //3
}
}
}
静态内部类
有static
修饰,实例化时可以不需要实例化外部类,外部类可以通过内部类.属性
访问静态成员,通过new 内部类()
访问静态内部类实例,举例:
public class Test {
public static void main(String[] args) {
AAA a = new AAA();
a.getB();
AAA.BBB b = new AAA.BBB(); // 实例化静态内部类
}
}
class AAA {
public int a = 1;
void getB() {
System.out.println("内部类变量:" + BBB.b); //访问静态内部类的静态属性
}
static class BBB {
static int b = 2;
}
}
匿名内部类
一次性使用的类,比如监听器,匿名内部类没有访问修饰符和构造方法,通过new 类名(){}
定义,举例:
public class Test {
public void xyz(AAA aaa) {
aaa.abc();
}
public static void main(String[] args) {
new test().xyz(new AAA() { //匿名内部类
public void abc() {
System.out.println("aaa");
}
});
}
}
interface AAA {
void abc();
}
局部内部类
在方法内部定义的类,作用域仅在该方法内,举例:
public class test {
public void xyz() {
class ABC{ //局部内部类
int a = 1;
}
System.out.println(new ABC().a);
}
public static void main(String[] args) {
new test().xyz();
}
}
注解
用于描述程序如何运行,以及在何时运行
注解语法
@xxx
基本注解
1.@Override
:检查是否重写方法,如果该方法没有重写(名字写错等),则会报错
2.@Deprecated
:说明过时方法
3.@Suppress Warnings("警告类型")
:消除某种类型的警告,如果有多种警告类型可以传入一个数组,all代表不显示所有警告,举例:
@SuppressWarnings({"unused","deprecation"}) //消除无用属性和过时方法的警告
public void test() {
int i = 0;
new Date().toGMTString();
}
自定义注解
1.注解本质就是一个继承了Annotation
接口的接口
2.注解中可以有属性,也可以有方法,但一般不用属性,只写方法
3.一般将注解中的方法称为属性,方法返回值称为类型
步骤
1.声明一个注解:@interface 注解名{}
2.往注解接口里定义属性,其中允许的属性类型如下:
(1)基本数据类型
(2)String
(3)枚举类型
(4)注解类型
(5)Class类型
(6)一维数组类型
3.通过@注解名
来添加注解,并实现属性类型
示例
public @interface AnnotationTest {
int i(); //基本数据类型
String s(); //String
String a() default "default"; //default关键字设置默认值
Class Test() default com.test.Test.class; //Class类型
AnnotationTest2 anno2(); //注解类型
}
@interface AnnotationTest2{
int[] j(); //一维数组类型
}
在方法中使用注解如下:
@AnnotationTest(i=1,s="aaa",anno2=@AnnotationTest2(j={1,2,3})) //默认值的可以不用设置,其他的都要设置
元注解
即只能修饰注解的注解
常用元注解
1.@Retention
:改变注解的存活范围
2.@Target
:注解能作用的对象
3.@Documented
4.@Inherited
:说明该注解可以被继承
网友评论