目录
初识面向对象
我们知道Java是一门纯面向对象的语言,那么何为面向对象呢?
拿洗衣服这件事来说,这个过程是人将衣服放进洗衣机,人将洗衣粉倒入洗衣机,人启动洗衣机,衣服就在洗衣机的作用下洗干净并甩干。整个过程中涉及到的对象有人,衣服,洗衣粉,洗衣机。
面向对象所注重的就是过程中对象与对象之间的交互,而不关心洗衣粉的过程(洗衣机是如何工作的),即面向对象是以功能来划分问题,而不是以步骤解决的。
类的定义
Java中一切皆对象,而对象的创建需要对应的模板,这种用来对一个实体(对象)进行描述的模板就称作类。
下面为要创建一个洗衣机的类的定义:
class WashMachine{ public String brand; // 品牌 public String type = “123”; // 型号(就地初始化) public double weight; // 重量 public double length; // 长 public double width; // 宽 public double height; // 高 public String color; // 颜色 public void washClothes(){ System.out.println("洗衣功能");// 洗衣服 } public void dryClothes(){ System.out.println("脱水功能");// 脱水 } public void setTime(){ System.out.println("定时功能"); // 定时 } }
其中class为类的关键字,brand,type,weigth,length,width,height,color为成员属性(成员变量),washClothes(),dryClothes(),setTime()为成员方法。
注:
1.类名建议使用大坨峰
2.一般一个文件当中只定义一个类
3.Eclipse会在public修饰的类中找main方法,因此main函数一般在public修饰的类中
4.public修饰的类必须和文件名相同
5.在声明变量的同时可以对成员属性直接给出初始值,这种行为叫就地初始化
类的实例化
定义好一个类就相当于定义了一个自定义的新类型,与Java的内置类型byte,short,int类似,不过类是引用数据类型,需要用new配合类名来实例化对象。
下面是创建一个洗衣机对象及对象成员的访问的使用例子:
class WashMachine{ public String brand; // 品牌 public String type; // 型号 public double weight; // 重量 public double length; // 长 public double width; // 宽 public double height; // 高 public String color; // 颜色 public void WashClothes(){ System.out.println("洗衣功能");// 洗衣服 } public void dryClothes(){ System.out.println("脱水功能");// 脱水 } public void SetTime(){ System.out.println("定时功能"); // 定时 } } public class Test { public static void main(String[] args) { WashMachine washMachine = new WashMachine();//实例化一个洗衣机对象 washMachine.brand = "huaxia";//该对象品牌为huaixia washMachine.color = "red";//该对象颜色为red washMachine.SetTime();//访问定时功能 } }
创建对象用new,访问成员方法或属性用.
注:
当对象空间被申请好之后,对象中包含的成员已经设计好了初始值(byte默认值为0,插入默认值为'\u0000',short默认值为0,int默认值为0,long默认值为0L,boolean默认值为false,float默认为0.0f,double0.0,引用类型为null)
this引用
在认识this引用之前我们先来看一下下面这段代码:
class Te { public int a; public void seDate(int a){ a = a; } public void printDate(){ System.out.println(a); } } public class Test{ public static void main(String[] args) { Te te = new Te(); te.seDate(2) ; te.printDate(); } }
我们可以发现上述代码中成员属性与形参名相同,那么函数体当中到底是谁给谁赋值?
运行完成后可以发现结果为0,可见函数体当中是形参给形参赋值(就近原则),那么如何让形参给成员属性赋值呢?
这就得用到this引用了
this 引用指向当前对象 ( 成员方法运行时调用该成员方法的对象 ) ,在成员方法中所有成员变量的操作,都是通过该引用去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。将上面代码改成如下代码就可以让形参给成员属性赋值:class Te { public int a; public void seDate(Te this,int a){ //这里的Te this可以省略不写 this.a = a; } public void printDate(Te this){ //这里的Te this可以省略不写 System.out.println(this.a); } } public class Test{ public static void main(String[] args) { Te te = new Te(); te.seDate(2) ; te.printDate(); } }
注:上面代码中的Te this参数一般不写,编译器会默认提供.
当一个类的属性(成员变量)名与访问该属性的方法参数名相同时,则需要使用 this 关键字来访问类中的属性,以区分类的属性和方法中的参数。
this引用还可以让类中一个方法,访问该类里的另一个方法或实例变量
class Te { public int a; public void seDate(Te this,int a){ this.a = a; this.printDate();//调用了te对象中的printDate函数 } public void printDate(Te this){ System.out.println(this.a); } } public class Test{ public static void main(String[] args) { Te te = new Te(); te.seDate(2) ; } }
注:
1.大部分时候,一个方法访问该类中定义的其他方法、成员变量时加不加 this 前缀的效果是完全一样的。
2.对于 static 修饰的方法而言,可以使用类来直接调用该方法,如果在 static 修饰的方法中使用 this 关键字,则这个关键字就无法指向合适的对象。所以,static 修饰的方法中不能使用 this 引用。并且 Java 语法规定:静态成员不能直接访问非静态成员。
this引用还可以用来访问本类的构造方法(构造方法是类的一种特殊方法,方法名称和类名相同,没有返回,等下就会介绍到)
public class Student { String name; // 无参构造方法 public Student() { //system.out.println(name);//第一个注释取消,编译失败 this("张三"); } // 有参构造方法 public Student(String name) { this.name = name; } public void printDate() { System.out.println("姓名:" + name); } public static void main(String[] args) { Student stu = new Student(); stu.printDate(); } }
注:this访问构造方法不能在普通方法中使用,只能写在构造方法中,并且在构造方法中使用时,必须是第一条语句且不能循环调用。
对象的构造
我们知道在Java方法内部定义一个局部变量时,必须进行初始化,否则会编译失败。但字段在声明之后没有给值会进行默认初始化,任然可以使用。
其实不论是默认初始化还是就地初始化都会用到一个方法------构造方法。
构造方法 ( 也称为构造器 ) 是一个特殊的成员方法,名字必须与类名相同,在创建对象时,由编译器自动调用,并且在整个对象的生命周期内只调用一次。public class Student { String name; // 无参构造方法 public Student() { this("张三"); } // 有参构造方法 public Student(String name) { this.name = name; } public void printDate() { System.out.println("姓名:" + name); } public static void main(String[] args) { Student stu = new Student(); stu.printDate(); } }
上述代码中,
就是两个构造方法,可以看出构造方法也可以构成方法的重载 ,并且还可以利用带有参数的构造方法对对象中的成员初始化指定的值。
我们在创建类的时候可以显示定义构造方法也可以不显示定义,当没有显示定义构造方法时,编译器会提供一份默认的不带参数的构造方法,但当我们显示定义了构造方法则编译器便不会提供默认的构造方法。
如:
class A{ public int a; public double b; } public class Test{ public static void main(String[] args) { A a = new A(); System.out.println(a.a + " " + a.b); } }
输出结果为0 0.0仍会调用构造方法进行初始
再如:
class A{ public int a; public double b; public A(int a){ this.a = a; } } public class Test{ public static void main(String[] args) { A a = new A(); //编译报错 System.out.println(a.a + " " + a.b); } }
new A()调用的是无参构造,因为A中定义了一个有参构造,故编译器不提供默认的构造方法,所以会编译报错
注:
1.构造方法名必须与类名相同,不能有返回值
2.构造方法一般都用public来修饰,少数情况下会被private修饰
3.代码编译完成后,编译器会将所有给成员初始化(就地初始化)的这些语句添加到各个构造函数中
封装
封装是面向对象的特性之一,他是通过将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
就好比一台组成复杂的计算机,它是通过外壳包装后将简单的开关机,显示器,键盘输入,USB接口等提供给我们,让我们使用计算机在不关注内部实现细节的基础上就可以与计算机实现简单的交互。
访问权限修饰符
Java 中主要通过类和访问权限来实现封装: 类可以将数据以及封装数据的方法结合在一起 ,更符合人类对事物的认知,而访问权限用来控制方法或者字段能否直接在类外使用 。 Java 中提供了下面四种访问限定符:
NO 范围 private 默认 protected public 1 同一包中的同一类 √
√
√
√
2 同一包中不同类 √
√
√
3 不同包中的子类 √
√
4 不同包中非子类 √
private : 在同一类内可见。使用对象:变量、方法,不能修饰外部类。
默认: 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
protected : 对同一包内的类和所有子类可见。使用对象:变量、方法,不能修饰外部类。
public : 对所有类可见。使用对象:类、接口、变量、方法
包
包的概念
Java中 为了更好的管理类,把多个类收集在一起成为一组,这个组就叫作 包。包是对类、接口等的封装机制的体现,是一种对类或者接口等的很好的组织方式(一个包中的类不想被其他包中的类使用),在同一个工程中允许存在相同名称的类,只要处在不同的包中即可 。包中类的使用
Java中提供了许多可供我们使用的类,我们通过以下方法就可以使用它们
1.直接使用public class Test { public static void main(String[] args) { java.util.Date date = new java.util.Date();//需要说明是哪个包中的哪个类 //得到一个毫秒级的时间戳 System.out.println(date.getTime()); } }
2.用import导入具体类
import java.util.Date;//用import导入某一包中的某一类 public class Test{ public static void main(String[] args) { Date date = new Date(); System.out.println(date.getTime()); } }
3.用import导入具体类
import java.util.*;//导入java.util这个包 public class Test { public static void main(String[] args) { Date date = new Date(); System.out.println(date.getTime()); } }
但使用这种方式如果导入的两个包中有相同类,在使用这个类时,为了避免冲突,仍需要按1方法来使用
import java.util.*; import java.sql.*; public class Test { public static void main(String[] args) { java.util.Date date = new java.util.Date(); System.out.println(date.getTime()); } }
注:导入包并不会一开始就把这个包全导入,而是当要用到某一类的时候才导入某一类
4.用import static导入包中静态的方法和字段
import static java.lang.Math.*;//导入Math类中被static修饰的静态方法 public class Test { public static void main(String[] args) { double x = 30; double y = 40; //静态导入的方式 double result = sqrt(pow(x,2) + pow(y,2));//sqrt和pow均为对应类下的静态方法 //非静态导入的方式 double result2 = Math.sqrt(Math.pow(x,2) + Math.pow(y,2)); } }
注:java.lang:系统常用基础类(String、Object),此包从JDK1.1后自动导入
自定义包
自定义包是我们使用者自己创建的,在包中创建的类会有package语句指定代码在哪个包中
,如果一个类没有 package 语句, 则该类被放到一个默认包中.。
static成员
我们定义的一个类中的成员属性和方法在没有static修饰的时候每个对象都会包含一份,而经过static修饰的静态成员不依赖于类的特定实例,被类的所有实例共享,就是说 static 修饰的方法或者变量不需要依赖于对象来进行访问,只要这个类被加载,Java 虚拟机就可以根据类名找到它们。
static修饰成员变量
被static修饰的成员变量叫作静态成员变量(类变量),静态成员变量不依赖于具体对象,是所以对象共享的。
静态成员变量既可以通过对象访问(推荐),也可以通过类名访问
class A{ public static int x; } public class Test{ public static void main(String[] args) { //直接通过类名访问 A.x = 1; System.out.println(A.x); A a = new A(); //通过对象访问 a.x = 2; System.out.println(a.x); } }
注:类变量存储在方法区中,生命周期伴随类的一生
static修饰成员方法
被static修饰的成员方法叫作静态成员方法(类方法),静态成员方法同样不属于某个具体对象。
静态成员方法既可以通过对象访问(推荐),也可以通过类名访问
class A{ public static void x(){ System.out.println("hhh"); } } public class Test{ public static void main(String[] args) { A.x();//通过类名访问 A a = new A(); a.x();//通过对象访问 } }
注:不能在静态方法中访问任何非静态成员变量,也不能在静态方法中调用任何非静态方法(静态方法不依赖对象,而非静态成员变量和方法都会有与对象相关的this)
static成员变量初始化
1.就地初始化
class A{ private static int x = 1;//就地初始化 }
2.静态代码块初始化
class A{ private static int x; //静态代码(等下会讲到)块初始化 static{ x = 1; } }
代码块
Java中将使用{}定义的一段代码称之为代码块。
1.普通代码块
普通代码块是定义在方法中的代码块
public class Test{ public static void main(String[] args) { //直接使用{}定义,普通方法块 { int x = 10 ; System.out.println("x1 = " +x); } int x = 100 ; System.out.println("x2 = " +x); } }
2.构造代码块
构造代码块也叫 实例代码块是定义在类中的代码块(不加修饰符)构造代码块一般用于初始化实例成员变量class Student{ //实例成员变量 private String name; private String sex; private int age; //实例代码块 { this.name = "hhh"; this.age = 12; this.sex = "man"; } public void show(){ System.out.println("name: "+name+" age: "+age+" sex: "+sex); } } public class Test { public static void main(String[] args) { Student stu = new Student(); stu.show(); } }
3.静态代码块静态代码块是使用 static 定义的代码块。一般用于初始化静态成员变量class Student{ //实例成员变量 private static String name; private static String sex; private static int age; //静态代码块 static{ name = "hhh"; age = 12; sex = "man"; } public void show(){ System.out.println("name: "+name+" age: "+age+" sex: "+sex); } } public class Test { public static void main(String[] args) { Student stu = new Student(); stu.show(); } }
注:
1,静态代码块不管生成多少对象都只执行一次。
2.静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
3.如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行(合并)4.实例代码块只有在创建对象时才会执行5.执行先后顺序:静态代码段→实例代码段→构造方法
内部类
内部类是封装的一种体现,它是指将一个类定义在另一个类或者一个方法的内部。(内部类和外部类共用一个java源文件,但编译后会形成新的字节码文件)
内部类的分类
内部类可以根据内部类定义的位置不同,分为成员内部类和局部内部类。1. 成员内部类 ( 普通内部类:未被 static 修饰的成员内部类 和 静态内部类:被 static 修饰的成员内部类 )2. 局部内部类 ( 不谈修饰符 ) 、匿名内部类class OutClass2 { // 成员位置定义:未被static修饰 --->实例内部类 public class InnerClass1 { } // 成员位置定义:被static修饰 ---> 静态内部类 static class InnerClass2 { } public void method() { // 方法中也可以定义内部类 ---> 局部内部类:几乎不用 class InnerClass5 { } } }
实例内部类
实例内部类是指未被static修饰的成员内部类。
在实例内部类中可以直接访问外部类中任意访问限定符修饰的成员class OutClass{ private int a; static int b; public void methodA(){ System.out.println(a); } public static void methodB(){ System.out.println(b); } class InnerClass{ int c; public void method2(){ //访问外部类中任意访问修饰限定符的成员 a = 1; b = 2; methodA(); methodB(); } } }
注:若实例内部类中有同名的优先访内部类自己的,如果要访问外部类的必须用外部类类名.this.同名成员来访问(实例内部类包含一个指向外部类对象的引用)
实例内部类的创建必须在有外部类对象的前提下才能创建
public class OutClass{ private int a; class InnerClass{ int b; } public static void main(String[] args) { //实例内部类的第一种创建方法 OutClass.InnerClass innerClass = new OutClass().new InnerClass(); //实例内部类的第二种创建方法 OutClass outClass = new OutClass(); OutClass.InnerClass innerClass1 = outClass.new InnerClass(); } }
外部类中访问内部类需要创建实例内部类的对象public class OutClass{ private int a; class InnerClass{ private int b = 1; } public void method(){ //必须先创建一个内部类对象 InnerClass innerClass = new InnerClass(); System.out.println(innerClass.b); } public static void main(String[] args) { OutClass outClass = new OutClass(); outClass.method(); } }
静态内部类
被static修饰的内部成员类称为静态内部类。
静态内部类中一般只能访问外部类的静态成员,如果要访问需要new一个外部类对象
public class OutClass { private static int a; private int b; public static void method1(){ System.out.println(a); } public void method2(){ System.out.println(b); } static class InnerClass{ public void method2(){ a = 1; //不能访问非静态成员b method1(); //不能访问非静态方法method2 //可以通过new一个外部类对象进行访问 OutClass outClass = new OutClass(); OutClass.b = 2; } } }
静态内部类的创建可以不需要创建外部类来访问
public class OutClass { private static int a; static class InnerClass{ int b; } public static void main(String[] args) { //静态内部类对象的创建&成员访问 OutClass.InnerClass innerClass= new OutClass.InnerClass(); System.out.println(innerClass.b); } }
局部内部类
局部内部类是定义在外部类的方法体或者 {} 中,该种内部类只能在其定义的位置使用,一般使用的很少局部内部类只能在所定义的方法体中使用,且不能被public,private等修饰符修饰public class OutClass{ int a; public static void main(String[] args) { //定义在方法体内的局部内部类 class InnerClass{ public void method() { System.out.println("hhh"); } } InnerClass innerClass = new InnerClass(); innerClass.method(); } }
对象的打印
在Java中所类都是Object的子类,Object中有一个toString方法,打印的时候会调用该方法,我们可以通过重写toString继而打印出对象的信息
下面是没重写toSring的代码:
public class Test{ String name; String age; Test(String name, String age){ this.name = name; this.age = age; } public static void main(String[] args) { Test test = new Test("小红","12"); System.out.println(test); } }
打印结果为Test@1b6d3586(实现类类名 + @ + hashCode)
重写toString后的代码如下:
public class Test{ String name; String age; Test(String name, String age){ this.name = name; this.age = age; } //重写的toString方法 public String toString(){ return "[" + name + "," + age + "]"; } public static void main(String[] args) { Test test = new Test("小红","12"); System.out.println(test); } }
打印结果为[小红,12]