数组
是用来存放相同类型的数据。
int[] a;//需要注意这里是申明数组
//int[5] a;这种是错的,坚决不可以在申明时指定数组大小。
a = new int[5];//这里才是创建数组
int[] b = new int[5];//这种是申明带创建
int[] c = {1,2,3};
该语句声明了包含3个元素的整型数组,编译器会根据花括号内指定的初始化器的数量决定数组的大小,当JVM在内存中加载代码的时候,会自动分配内存位置进行初始化。
这类初始化称之为集初始化,是一种非常安全的用于初始化数组的方法,因为在运行时初始化,比较容易报错。
另外如果有大量的数据,请不要使用这种方式申明,因为Java编译器会创建大量的Java字节码用来初始化数组,最好的方法就是存在外部的文件中,然后在运行的时候,进行读取。
Tip
我们一定要正确规范我们平常代码书写,争取做代码的艺术家,我们要习惯在一些不变的量的时候,创建常量,如果这个值会发生变化,那么只需要修改一处即可。
非矩形数组
在这之前,我们不论是一维数组还是多维数组,最起码都是矩形数组。
非矩形数组在分配内存时候,会根据数组的行数先分配几个连续的空间,然后在每一个单元上指向一维数组的引用,下面是具体的代码:
int[][] a = new int[5][];
a[0] = new int[5];
a[1] = new int[2];
a[2] = new int[1];
a[3] = new int[7];
a[4] = new int[9];
//在引用的时候,还是a[i][j]去引用,注意索引,不要越界。
封装
封装使我们面向对象的三大特点之一,我们private申明符隐藏,通过暴露set、get方法来提供访问。在这里需要提到的一点,我们常常对一些属性值都要要求,比如年龄不能为负,等等。那么这些检验,你就需要在每次赋值的时候,都进行判断,这实际上是很不友好的,我们可以在set方法做出判断,好处就是下次判断修改了,只需要改一处即可。
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
if(age>0) {
this.age = age;
}else {
System.out.println("用户年龄输入不合适!");
}
}
构造函数初始化赋值
尽管我们在显示申明某个字段之后,编译器会默认赋予值,但是如果你仍然希望你创建的对象具有初始化的默认值,可以在构造函数中进行赋值,这样一旦构造对象,它就会有默认值。
public Test36() {
super();
age = 10;
// TODO Auto-generated constructor stub
}
如果要每次传入属性的值不同,你只需要为构造函数提供不同参数,每次构造的时候,赋不同的值。
记住,只要你提供了自己的构造函数,编译器都不会提供默认的构造函数,如果自己需要用,自己显示声明一下。
package
package语句可以让你将相关的类逻辑性分组到单个独立单元中。
需要注意的地方:
- 包必须早于任何其他语句在源文件的开头进行申明。(在源程序的其他地方编写package申明语句会导致编译时错误)
- 源文件只允许包含一条声明。
- 包名必须为层次结构。(子包必须由点分隔)
- 如果没有申明包,编译器会创建默认包,而所有不声明的类都会放到默认包中。(创建的默认包就是当前目录本身)
import
紧接在package声明之后的是import申明,使用import语句可以告诉编译器在编译时去哪里找到源程序需要的外部类。
在import语句中,可以指定类名来导入单个类,通过号,可以导入所有的类,import为编译器指定寻找类的路径。import并不会加载代码,因此带的import语句不会影响程序的运行的性能。
继承
子类拥有父类的属性和方法,除了是私有的。(前提是自爱一个包中)
super关键字,在继承中是常见的,可以用来子类对象访问父类的方法、属性,除了私有(前提是在一个包中)
public Test38() {
super();
// TODO Auto-generated constructor stub
}
在继承中,我们经常看见一个继承父类的一个方法,经常会自带super(),super( )这个是执行父类的这个方法,需要注意这个必须是在第一行中。如果你在之类不需要执行父类的,删除即可。
在构造函数中,子类继承父类,只有你在构造函数中申明对其他函数的调用,那么不会默认调用父类的构造函数,否则,只要调用子类的构造函数,父类的构造函数按定义的层次去调用,这就是构造函数链。
如果不对super做显示调用,编译器将会提供不带参数的super调用,如果父类没有显示提供无参的构造函数,会编译报错,但是如果父类没有无参有参的构造函数,编译器会调用自己(子类)的无参函数。
supper和this不能同时出现,this和super一样,出现都是在第一行,否则编译出错。
方法重载
就是多个方法,方法名相同,但是方法的参数个数和类型不相同。
构造函数的复制
就是通过在构造函数传入之前构造对象的引用,在进行赋值。
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Test39(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Test39(Test39 t) {
this(t.name,t.age);//调用当前对象的构造函数
//等价于this.name = t.name,this.age = t.age
// TODO Auto-generated constructor stub
}
public static void main(String[] args) {
Test39 t = new Test39("laoqiang",123);
Test39 t1 =new Test39(t);
System.out.println(t1.age+t1.name);
}
final
根据Java编程习惯,所有final变量都应当以大写字母声明,并且使用下划线字符分隔变量名的单词。
final变量不需要在声明时候初始化,也可以在申明后其他地方初始化,不过,初始化只能执行一次,声明为初始化的final变量叫空缺final变量。最好初始化在构造函数中。
final Test39 t = new Test39("laoqiang",12232131);
t.setName("liu");t.setAge(25);
System.out.println(t.getAge()+t.getName());
//t = new Test39("sdadsa",12323);被final修饰的对象,
//只能保证他的引用不能变
//但是它的对象值是可以改变,可以通过暴露出来的set方法修改
//如果没有加final,只是这个t对象,引用改变了。
//注意这种方式和Test39 t1 = new Test39(t);
//Test39 t1 = new Test39(t);这种是对象重名了。
访问修饰符
在继承中,不能使用比父类更弱的权限,最弱private。
static
static 关键字来创建全局访问的变量。静态字段的内存分配仅发生一次,就是在类加载的时候,静态变量在这个类的所有对象中可以共用,静态变量的值的修改,在所有变量中都是可见的。
理解static字段最好的例子:统计构造了多少个对象。
private static int count = 0;
public static void main(String[] args) {
Test40 t1 = new Test40();
Test40 t2 = new Test40();
Test40 t3 = new Test40();
Test40 t4 = new Test40();
Test40 t5 = new Test40();
Test40 t6 = new Test40();
Test40 t7 = new Test40();
System.out.println(count);
}
public Test40() {
super();
// TODO Auto-generated constructor stub
count++;
}
在继承中,子类会继承父类的静态变量,可以说是和父类共用一个静态变量。
- 静态字段属于类而不是对象。非静态变量属于类的实例,因此被称之实例变量。
- 静态变量只在执行时初始化一次。
- 静态变量直接类名调用。
静态方法
- 静态方法是通过类来引用,也可以通过对象来调用,这种是不建议的。
- 静态方法中是不能this和super关键字。
- 静态方法中是不能访问非敬爱字段和方法,只能访问静态字段和方法。
- 静态方法不能被子类重写。
静态初始化器
我们常常用构造函数来初始化类的非静态字段,构造函数在对象创建时被调用,并在对象可用之前完成其中定义的初始化。我们使用同样的构造函数来初始化类的静态字段,在构造函数中初始化静态字段意味着就必须等待对象创建完。在某些情况下,你希望在实例化之前初始化静态字段,这时就必须使用静态初始化器,需要注意只能初始化静态变量。
静态初始化器类似于没有名字、没有参数、没有返回类型。
静态初始化器是在类加载时自动执行的。
静态初始化器仅在加载类的时候执行一次,类可以包含多个静态初始化器,他们按照在类中出现的顺序执行,所以按照一定的顺序组织累的多个逻辑初始化是非常有用的。
注意点:
- JVM将静态代码块大小限制64kb,所以不能放太多代码。
- 静态代码块中代码,不能抛出检查。
- 静态初始化不能使用this,因为还没有实例。
- 不能在静态代码块显示调用super,只有对象创建的时候,才可以调。
接口
Java接口类似于协议,提供不同类型或者相同类型对象之间一致同意的行为。
在Java中不包含任何方法的接口被称之为标记接口,例如:Serializable。
接口中的修饰符,只会使public或者默认的。在接口中申明的是常量字段或者是抽象方法,没有方法体,不需要实现。接口默认就是抽象,不需要显示用abstract关键字。
public interface Test44 {
int age = 100;
void show();
}
拓展接口:
Java的接口是可以通过extends关键字去拓展接口中方法的能力。
第一个接口:
public interface Test44 {
int age = 100;
void show();
}
第二个接口:
public interface Test45 extends Test44 {
@Override
default void show() {
// TODO Auto-generated method stub
System.out.println("我是拓展接口");
}
void look();
}
实现类:
@Override
public void look() {
// TODO Auto-generated method stub
System.out.println("我是实现");
}
public static void main(String[] args) {
Test46 t = new Test46();
t.look();
t.show();
}
抽象类
抽象类的注意点:
- 一个抽象类中可以没有一个抽象方法,但是只要有一个就必须将类申明成抽象的。
- 抽象类不能被实例化,但是他的子类可以被实例化。
- 抽象类除了抽象方法,也可以有方法的实现。
- 抽象方法的修饰符应该用public和protected,不能用private,原因子类继承抽象类,但是不继承抽象类的私有的方法。
- 抽象函数可以用构造函数。
内部类
- 静态类
静嵌套类不可以引用外层累的非静态方法
- 非静态类(成员类)
- 局部类
定义在方法体内的,被称之为局部类。局部类的作用域就是方法的作用域。同时也可以定义在静态初始化器和构造函数中。
构造函数中的局部类
public Test54() {
super();
// TODO Auto-generated constructor stub
class inner{
public void show() {
System.out.println("我是构造函数中的局部类");
}
}
new inner().show();//直接在构造函数中构造对象,调用对应的方法。
}
public static void main(String[] args) {
Test54 t = new Test54();
}
方法体中的局部类
private int a =5;
public void show(final int b){
//b = 20;
//final int d = 0;
//d = 50;
class inner{
private int c = 20;
public void test() {
System.out.println(a);
System.out.println(b);//这里内部类访问方法的形参或
//者局部变量,必须申明成final
System.out.println(c);
//System.out.println(d);测试内部类访问局部变量,也必须加final
}
}
new inner().test();//直接在方法中进行构造对象,执行相应的方法。
}
public static void main(String[] args) {
Test55 t = new Test55();
t.show(10);
}
方法中局部类
private int a =5;
public void show(final int b){
//b = 20;
//final int d = 0;
//d = 50;
class inner{
private int c = 20;
public void test() {
System.out.println(a);
System.out.println(b);//这里内部类访问方法的形参或
//者局部变量,必须申明成final
System.out.println(c);
//System.out.println(d);测试内部类访问局部变量,也必须加final
}
}
new inner().test();//直接在方法中进行构造对象,执行相应的方法。
}
public static void main(String[] args) {
Test55 t = new Test55();
t.show(10);
}
局部类的注意点:
- 局部类仅在自身的代码块中可见和可用,这就是为啥在构造函数和方法里直接构造局部类的对象,进行调用。
- 局部类可以访问局部内的任何变量、方法参数、或者是作用域的异常参数,但是他们必须是final修饰。
- 局部类不能申明包含static的字段、方法等。
局部类不能用public、protected、private、static修饰。
- 匿名类
是在方法体中申明的内部类,没有名称。
具体例子:
public abstract class Test57 {
abstract void eat();
}
//这里可以是抽象类或者是接口都ok
public static void main(String[] args) {
new Test57() {
@Override
void eat() {
// TODO Auto-generated method stub
}
};//这里实际匿名构造一个类继承了Test57
}
匿名类的注意点:
- 匿名类不能有构造函数,因为没有与之相关的名字。
- 匿名类编译之后,每一个匿名类会产生Outclassname$1.class文件,如果有多个匿名类,更改后面的数字,递加。
内部类可以访问修改外部类的成员变量
private int count;
class inner{
private void setCount() {
count++;
}
}
public void getCount() {
System.out.println(count);
}
public static void main(String[] args) {
Test50 t = new Test50();
Test50.inner ti = t.new inner();
ti.setCount();
ti.setCount();
t.getCount();
}
内部类的注意点
- 内部类和外部类的名字不可以相同。
- 在编译外部类的时候,编译器会为每一个内部类生成单独的.class文件,.class文件名字为outclassname$innerclassname。
- 内部类可以使用外部类的类和实例变量。
- static 只能用于内部类,不能应用于外部类。
- 在内部类中不能申明static成员变量,除非内部类声明为static。
内部类生成对象方法
- 静态内部类
Test53.inner ti = new Test53.inner();
- 非静态内部类
Test51 t = new Test51();
Test51.inner ti = t.new inner();