基础
Java本身
JAVA SE-----Java标准版平台
jdk-----Java开发工具包
jre-----Java运行的环境
jvm-----Java虚拟机(实现跨平台,移植)
javac
编译命令
Java
执行命令
javap -c + java
已编译文件名 分析程序
Java编译程序:javac.exe
字节码是一种虚拟的机器代码,不针对特定的机器,可以支持在java的任意平台上运行
java解释器:java.exe
负责将字节码解释成本地机器指令代码并运行。每个平台都有相应的java解释程序。
jdk和open-jdk
openjdk是jdk开放的原始码版本,以GPL协议的形式放出,而jdk采用JRL放出。虽然两个协议都是开源的,但是在使用上的不同在于GPL V2允许在商业上使用,而JRL只允许个人研究使用。
open-jdk不包含Deployment(部署)功能,并且代码也不完整、只包含最精简的JDK
标识符
1.标识符字母 数字 连字符 货币符号可以组成
2.第一个字符必须是字母 连字符 货币符号
3.区分大小写
4.标识符不能与关键字相同
5.标识符不能是true false null
关键字
关键字
abstract | assert | boolean | break | byte |
---|---|---|---|---|
case | catch | char | class | const |
continue | default | do | double | else |
enum | extends | final | finally | float |
for | goto | if | implements | import |
instanceof | int | interface | long | native |
new | package | private | protected | public |
return | strictfp | short | static | super |
switch | synchronized | this | throw | throws |
transient | try | void | volatile | while |
java语言中,true、false和null不是关键字,但是也不能用作其他用用途。
Java数据类型
变量就是申请内存来存储值。
Java没有无符号数据类型
内置数据类型
byte | 8位 | 有符号 | 以二进制补码表示的整数 | -128~127 | 默认值0 | 可节省空间 |
short | 16位 | 有符号 | 以二进制补码表示的整数 | -32768~32767 | 默认值0 | 可节省空间 |
int | 32位 | 有符号 | 以二进制补码表示的整数 | -231~231-1(2 147 483 648-1) | 默认值0 | |
long | 64位 | 有符号 | 以二进制补码表示的整数 | -263~263-1 | 默认值0L | longa=1000L/l |
float | 32位 | 单精度 | 符合IEEE 754标准的浮点数 | 浮点数不能用来表示精确的值 | 默认值0.0f | |
double | 64位 | 双精度 | 符合IEEE 754标准的浮点数 | 浮点数不能用来表示精确的值 | 默认值0.0d | |
char | 16位 | Unicode字符 | \u0000(0)~\uffff(65 535) | |||
boolean | 只有两个取值 |
java里double型可以除0,不会报错,有无穷大和无穷小
数组
数组初始化
- java的数组变量是引用类型的变量,java的数组是静态的,即当数组被初始化之后该数组的长度是不可变的。
- java中的数组必须经过初始化才能够使用,初始化是指分配内存空间、指定初始值
简而言之,java数组需要初始化,初始化后长度不可变
数组的初始化有两种方式:
- 静态初始化:初始化时由程序员显式指定每个数组元素的初始值,由系统决定长度
- 动态初始化:初始化时程序员只指定数组长度,由系统为数组元素分配初始值
java数组有length
属性
java数组是静态的,一旦初始化完成,数组元素的内存空间分配即结束,程序只能改变数组元素的值,无法改变长度。
数组变量不是数组本身,它只是指向堆内存中的数组对象,因此可以改变一个数组变量所引用的数组,这样可以造成数组长度可变的假象。
数组变量是引用变量,数组对象和数组变量不一样。需要数组初始化的是数组对象。
java数组必须在初始化时给定长度,长度不可变
int[] nums = new int[5];
int[] nums = new int[]{
1,2,3};
int[] nums = {
1,2,3};
在java语言中定义数组变量时,不能定义其长度,只能在使用new关键字创建数组时定义长度。
Java中将数组作为引用类型,所以用new关键字创建对象,给数组分配内存
数组的复制
有两个数组,list1和list2,让list1=list2,结果并不会复制,而是让两个数组名都指向了同一个内容,即list2的内容。
如果想复制数组,可以通过逐个循环赋值
或者使用java.lang.System
里的arraycopy
arraycopy(sourceArray , srcPos , targetArray , tarPos , length);
srcPos
和 tarPos
表示的是两个数组开始的位置
length
是表明从源数组要复制多少个元素给目标数组
System.arraycopy(sourceArray , 0 , targetArray , 0 ,sourceArray.length);
不过这个方法并不会给目标数组分配空间,所以目标数组一定要已经有空间
可变长参数列表
public static void printMax(int a, double… numbers){
……}
….main…….
printMax(1,new double[]{
1,2,3});
- 可变长度参数只能有一个
- 必须是最后一个参数
字符串
字符串和String类
String s = "abc";
将常量池里的地址给了s,“abc”是一个常量,s是变量
- String类代表字符串。Java里的所有字符串如"abc"都作为此类的实例化实现
- 字符串是常量,它们的值在创建后不能更改,比如创建一个"abc"后是无法改变的
字符串的拼接
s+="def";
打印s得abcdef
这里+=其实相当于concat
方法 ,耗内存
s=s.concat("def");
该方法new了一个新的字符串返回,不在字符串池里
String s2="abc";
String s3 = new String("abc");
String s4 = new String("abc");
其中s2==s(s是上文的s)
s2 s3 s4地址不一样 new一个字符串和常量字符串地址不一样,
比较的时候比较的是地址
为什么String使用final修饰
Java中为了效率考虑,以“”包括的字符串,只要内容相同 循序、大小写相同,无论在程序代码中出现几次JVM都只会建立一个实例,放在字符串池(String pool)中维护
- 为了实现字符串池
- 为了多线程安全
- 字符串是不可变的,所以在它创建的时候HashCode就被缓存了,不需要重新计算,使得字符串很适合作为Map中的键,字符串的处理速度要快过其它的键对象
StringBuilder和StringBuffer
- 这两个类的对象能够被多次修改,并且不产生新的未使用对象
- StringBuilder类在 java 5 中被提出,该类的方法不是线程安全的(不能同步访问)
StringBuilder
StringBuilder是一个可变对象,可以预分配缓冲区
。
往StringBuilder中新增字符时,不会创建新的临时对象。
StringBuilder还可以进行链式操作
StringBuilder sb = new StringBuilder(100);
sb.append(“aaa”).append(“bbbb”).append(“hhh”);
String s = sb.toString();
可以链式操作是因为源码里的append()方法会返回this,就可以不断调用自身的其他方法。
StringBuffer
构造一个字符串缓冲区,其中没有字符,初始容量为16个字符。
为什么StringBuffer是安全的
看源码就能看出来了,StringBuffer的很多操作方法都是加了Synchronized关键字。
注意
对于普通的字符串+操作,并不需要我们将其改写为StringBuilder,因为Java编译器在编译时就自动把多个连续的+操作编码为StringConcatFactory的操作。
在运行期,StringConcatFactory会自动把字符串连接操作优化为数组复制或者StringBuilder操作。
StringBuffer是Java早期的一个StringBuilder的线程安全版本,它通过同步来保证多个线程操作StringBuffer也是安全的,但是同步会带来执行速度的下降。
StringBuilder和StringBuffer接口完全相同,现在完全没有必要使用StringBuffer。
方法的重载 (静态多态)
属于静态多态
方法签名:方法名和参数类型,不包括返回值类型和修饰符。
系统调用方法是根据方法签名调用。所以不能基于不同的返回类型或修饰符来重载方法。
在一个类中,方法的名字相同,但参数的个数、参数的类型或返回值类型不同
- 方法名一定要相同
- 方法的参数表必须不同,包括参数的类型或个数,以此区分不同的方法体。
- 如果参数个数不同,就不管它的参数类型了
- 如果参数个数相同,那么参数的类型必须不同
- 方法的返回类型、修饰符可以相同,也可不同
//overloading here, user should provide nickname
public static void SendM(String nickname) {
String[] contents = {
"How are you?Alice.",
};
SendM(nickname,"DefaultLand",contents[(int)(Math.random() \* 2)],3,1);//0-2
}
public static void SendM(String name, String servername, String message, int
count, int interval) {
**……**
}
修饰符/访问修饰符
类
访问修饰符
修饰符
class 类名称 extends
父类名称 implement
接口名称
(访问修饰符与修饰符的位置可以互换)
访问修饰符
public 可以被所有类访问(使用)
package 可以被同一个包中的类访问(使用) 默认的访问权限,可以省略此关键字
修饰符
final 使用此修饰符的类不能够被继承
abstract 声明抽象类
变量
- java中没有全局变量,只有方法变量、实例变量(类中的非静态变量)、类变量(类中的静态变量)。
- 方法中的变量不能有访问修饰符。
声明实例变量时,如果没有赋初值,将被初始化为null(引用类型)或者0、false(原始类型)。
实例变量初始化器是一个用{}
包含的语句块,在类的构造器被调用时运行,运行于父类构造器之后,构造器之前。
类变量(静态变量)也可以通过类变量初始化器来进行初始化,类变量初始化器是一个用static{}包含的语句块,只能被初始化一次。
访问修饰符
public 可以被任何类访问
protected 可以被同一包中所有类访问,可以被所有子类访问
private 只能够被当前类的方法访问
缺省/无访问修饰符 可以被同一包中的所有类访问,如果子类没有在同一个包中,也不能访问
修饰符
static 静态变量 可以被类的所有实例共享,不需要创建类的实例就可以访问静态变量
final 常值 值只能够分配一次,不能更改 建议和static一起使用
transient 告诉编译器,在类对象序列初始化时,此变量不需要持久保存
volatile指出可能有多个线程修改此变量,要求编译器优化以保证对此变量的修改能够被正确的处理
方法
访问修饰符
修饰符
返回类型 方法名(参数列表)throws 违例列表
类的构造方法不能够有修饰符、返回类型和throws子句
类的构造方法被调用时,首先调用父类构造方法,然后运行实例变量和静态变量的初始化器,然后才运行构造器本身。
如果构造器方法没有显示的调用一个父类的构造方法,那么编译器会自动为它加上一个默认的super(),而如果父类又没有默认的无参构造方法,编译器就会报错。super必须是构造方法的第一个子句。
访问修饰符
public 可以从所有类访问
protected 可以被同一包中的所有类访问,可以被所有子类访问
private 只能够被当前类的方法访问
缺省/无访问修饰符 可以被同一包中的所有类访问
修饰符
static 静态方法,并不需要创建类的实例就可以访问静态方法
final 防止任何子类重载该方法
abstract 抽象方法
native
synchronized 多线程的支撑
子类调用父类信息
- 使用super关键字
- 可以调用父类公有属性和方法
- 可以调用父类protected属性和方法
public 好像没啥不能访问的
(default) 子类不能访问,不同包不能访问,同包同类ok
private 只有同一类中可访问
无访问修饰符 包访问级别
protected
具有保护访问权限的成员可以在其所属类、其所属类的子类,及其所属包中访问。不同包内不能访问,可以被所有子类访问
当子类和父类不在同一个包中时,
- 父类成员被子类成员继承后,公用成员访问权限保持不变
- 私有成员和具有包访问权限的成员不能在子类中访问,也不能通过子类对象或子类名访问。
- 保护成员在子类可以直接访问,也能通过子类对象或子类名访问,但不能通过父类对象访问(如果是静态成员,可以通过父类名访问)
- 如果子类是共用类,保护成员在父类所属包中还可以通过子类对象或子类名访问。
final
成员变量:常量不可更改
final修饰的实例变量必须显式指定初始值,并且只能在以下3个位置指定初始值
- 定义final实例变量时指定初始值
- 在非静态初始化块中为final实例变量指定初始值
- 在构造器中为final实例变量指定初始值
经过编译器的处理,这三种方法都会被抽取到构造器中赋初始值。
final修饰的静态/类变量也必须显式指定初始值,且final类变量只能在2个地方指定初始值
- 定义final静态变量时指定初始值
- 在静态初始化块中为final类变量指定初始值
经过编译器的处理,这两种方法都会被抽取到静态初始化块中赋初始值
局部变量本来就需要显式地赋初始值,final修饰局部变量不能重新赋新值
修饰对象变量时,修饰的是对象的引用,不能引用另一个变量
方法:不可被重写
不过可以重载父类的方法
类:不可被继承
作用:防止扩展和重写
原理
final变量的赋值使用了putfiled指令,JVM会在该指令后加入写屏障,保证其他线程读到它的值时不会出现未初始化的情况。
我们知道写屏障和读屏障一个重要的特性就是禁止指令间的重排序,特别是对于final域来说,编译器和处理器都需要遵从如下的两条规则:
- 任意构造函数中对一个final域的写入,与随后把这个构造对象的引用赋值给另一个引用变量,这两个操作不能重排序。言外之意:在对象引用为任意线程可见之前,对象的final域已经被正确的初始化过了。
- 初次读一个包含final域对象的引用,与之后初次读这个final域,这两个操作之间不能重排序。言外之意:只有得到了包含final域对象的引用,才能后读到final域的值。
final关键字通过上述的两条规则,保证了在写final域和读final域时的正确性。
static
方法体(局部变量)和初始化块内声明的变量不能用关键字static修饰。
表示的是生命周期,静态属性/方法是先于类的实例存在的
static修饰的成员被类的所有对象所有,静态成员可以使用类名直接访问
例:public class Helloworld Helloworld.hobby
1.静态属性/变量
静态属性ab、成员变量bc
public class Apple{
public static int ab = 0;
private int bc;
}
每实例化一个对象都会有一个新的bc,但是ab所有对象共有一个
2.关于静态方法
static方法里不能用this,super
静态方法里没有相应的this引用
- 静态方法可以调用同类中的静态成员,但不能 直接 调用非静态成员
- 静态方法可以通过对象来访问非静态成员
普通方法可以直接调用静态成员
static只能修饰类里的成员,不能修饰外部类,不能修饰局部变量、局部内部类。
super 和 this
this
this表示当前类的对象,指向对象本身的指针
- 普通的直接引用
- 形参与成员重名,用来区分
- 引用自身其他构造方法
super
- 引用父类成员
- 子类中的成员变量或方法与父类中成员变量或方法同名
- 引用父类构造方法
super()必须写在子类构造方法的第一行
this()也需要放在构造方法的第一行
this 和 super 不能同时出现在一个构造函数里
!!!均不能在static环境中使用
this表示这个类的当前实例,super表示父类的当前实例
static是属于类的,this是类的一个对象。
注:
Construction call must be the first statement in a construction
构造调用必须是构造中的第一个语句 即super必须是第一句
子类构造中默认调用父类构造 super(); 先实例化父类对象再实例化子类
对象和类
import语句
java.lang这个包不需要导入,该包包含的是java语言的基础类,java程序会自动导入。
比如像java.lang.System
杂谈
像这种直接 new Dog();这种,经常出现。匿名对象,不多说了。
Role role1 = new Role();
Role role1:
1.declaration of role1 in main method's stack(栈)//在栈里声明对象
2.存了分配空间的地址
=new Role(): 1.allocated(分配) a space in the heap(堆) //在堆里分配空间
2.give role1 the address of the space. //将空间的地址给role1
(The address in Java virtual machine.not real address.)
3.用构造方法来初始化role1这个对象,为它赋值
(Role()作为构造方法是用来初始化属性的,用new再初始化对象,合在一起等同于3)
实例化对象
In object-oriented programming,
在面向对象的编程中,
the process of creating object with class is often referred to instantiation
通常把 用类创建对象 的 过程 称为实例化
类是抽象的,我们没有办法操作它或使用它的方法和属性
只有将这个类实例化称为一个对象,然后调用它的方法和属性
实例化对象就是由抽象到具体的过程,这个过程叫实例化
public class 与 class
类的访问权限
在编写类的时候可以使用两种方式定义
public class
class
public class 定义类:
如果一个类声明的时候使用了public
class进行了声明,则类名称必须与程序文件名称完全一致。并且被public修饰的类可以被其他包访问
class定义类
如果一个类声明的时候使用了class进行了声明,则作为启动类的名称可以与文件名称不一致,但是执行的时候肯定执行的是生成后的名称。
没有public修饰的类,该类有包访问权限,只能在该包中使用该类,不能被其他包访问
但是在执行的时候,执行的是生成后的.class文件,生成的的.class文件名称和class名称一样。
- 每个编译单元(文件)都只能有一个public类,即每个编译单元都有单一的公共接口,用public类实现。此时,main必须包含在public类中。
- public类的名称必须完全与含有该编译单元的文件名称一致,包括大小写。如果不匹配,编译时错误。
- 如果编译单元(文件)中不含有一个public类,此时编译单元文件名称可以与启动类名称不一致。即可以随意对文件命名。此时,main()不是必须要放在Public类中才能运行程序。
总的来说,一个java源文件中最多只能有一个public类,当有一个public类时,源文件名必须与之一致,否则无法编译。如果源文件中没有一个public类,则文件名可以随意取。
包含多个类的源程序文件经编译后,将会生成多个类(字节码)文件。类文件名称和源文件中类的名称一一对应并相同(每个都有自己独立的文件)java系统调用指定的那个类中的main方法。
设计栈类
栈堆是以先入先出的方式保存数据的数据结构。
栈有许多应用,例如编译器使用栈来处理(process)方法调用(invocations),调用(invoke)方法时,将其参数(parameters)和局部变量(local
variables)推入栈中。当一个方法调用(call)另一个方法时,新方法的参数和局部变量被推入栈。当一个方法完成它的工作并返回给它的调用者时,它的相关空间会从栈中释放(release)。
public class TestStackOfIntegers {
public static void main(String[] args) {
StackOfIntegers stack = new StackOfIntegers();
for(int i = 0;i< 10; i++)
stack.push(i);
while(!stack.empty())
System.out.print(stack.pop()+" ");
}
}
public class StackOfIntegers {
private int[] elements;
private int size;
private static final int DEFAULT_CAPACITY = 16;
public StackOfIntegers() {
this(DEFAULT_CAPACITY);
}
public StackOfIntegers(int capacity) {
elements = new int[capacity];
}
//将新的整数放入栈顶
public void push(int value) {
if(size >= elements.length) {
int[] temp = new int[elements.length*2];
System.arraycopy(elements, 0, temp, 0, elements.length);
elements = temp;
}
elements[size++]=value;
}
//返回并且去除栈顶的元素
public int pop() {
return elements[--size];
}
//返回栈顶的元素
public int peek() {
return elements[size - 1];
}
//测试栈是否是空的
public boolean empty() {
return size == 0;
}
//返回栈内有多少元素
public int getSize() {
return size;
}
}
包装类 wrapper class
JAVA的八种基本数据类型不面向对象,为每个基本数据类型设计了一个对应的类进行代表
int->Integer char->Character 其余大写第一个字母即可
–>boxing int n1=10; Integer N1=new Integer(n1);/Integer N2=n1;
–>unboxing int n2=N2.intValu