封装性是面向对象的核心思想。将对象的属性和方法封装起来,不需要让外界知道具体实现的细节,这就是封装的思想。封装可以使数据的安全性得到保证。当把过程和数据封装起来后,对数据的访问只能通过已定义的接口进行。
-
属性的封装。Java 中类的属性的访问权限的默认值不是 private,要想隐藏该属性或方法,就可以加 private(私有)修饰符来限制只能在类的内部进行访问。对于类中的私有属性,要对其给出一对方法(getXxxO和 setXxx0))访问私有属性,保证对私有属性的操作的安全性。
-
方法的封装。对于方法的封装,该公开的公开,该隐藏的隐藏。方法公开的是方法的声明(定义),即只要知道参数和返回值就可以调用该方法。隐藏方法的实现会使实现的改变对架构的影响最小化。完全的封装是类的属性全部私有化,并且提供一对方法来访问属性。
继承主要指的是类与类之间的关系。通过继承,可以效率更高地对原有类的功能进行扩展。继承不仅增强了代码的复用性,提高了开发效率,更为程序的修改补充提供了便利。Java 中的继承要使用 extends 关键字,并且 Java 中只允许单继承,即一个类只能有一个父类。这样的
继承关系呈树状,体现了 Java 的简单性。子类只能继承在父类中可以访问的属性和方法,实际上父类中私有的属性和方法也会被子类继承,只是子类无法访问。
多态多态是把子类型的对象主观地看作其父类型的对象,那么父类型就可以是很多种类型。编译时类型指被看作的类型,是主观认定的。运行时类型指实际的对象实例的类型,是客观的,不可改变(也被看作类型的子类型)。
多态有以下特性:对象实例确定后则不可改变(客观不可改变);只能调用编译时类型所定义的方法;运行时会根据运行时类型去调用相应类型中定义的方法。
2、类 定义类[修饰符] class 类名 {
零到多个构造器定义
零到多个成员变量
零到多个方法……
}
类的方法
方法定义在类中,他和类的成员属性一起构成一个完整的类,方法包含一个方法头和一个方法体,一个方法有4各要素。其定义一个方法的语法:
修饰符 返回值类型 方法名 (形参列表) {
方法体
return 返回值
}
-
修饰符:修饰符可以省略,也可以是 public、protected、private、static、final、abstract,其中 public、protected、private三个最多只能出现其中之一;abstract 和 final 最多只能出现其中之一,它们可以与 static 组合起来修饰方法。
-
方法返回值类型:返回值类型可以是 Java 语言允许的任何数据类型,包括基本类型和引用类型;如果声明了方法返回值类型,则方法体内必须有一个有效的 return 语句,该语句返回一个变量或一个表达式,这个变量或者表达式的类型必须与此处声明的类型匹配。除此之外,如果一个方法没有返回值,则必须使用 void 来声明没有返回值。
-
方法名:方法名的命名规则与成员变量的命名规则基本相同,但由于方法用于描述该类或该类的实例的行为特征或功能实现,因此通常建议方法名以英文动词开头。
-
形参列表:形参列表用于定义该方法可以接受的参数,形参列表由零组到多组“参数类型形参名”组合而成,多组参数之间以英文逗号(,)隔开,形参类型和形参名之间以英文空格隔开。一旦在定义方法时指定了形参列表,则调用该方法时必须传入对应的参数值——谁调用方法,谁负责为形参赋值。
成员变量用于定义该类或者该类的实例所包含的状态数据,语法格式如下:
修饰符 类型 成员变量名 = 默认值;
-
修饰符:修饰符可以省略,也可以是 public、protected、private、static、final,其中public、 protected、private三个最多只能出现其中之一,可以与static、final 组合起来修饰成员变量。
-
类型:类型可以是 Java 语言允许的任何数据类型,包括基本类型和现在介绍的引用类型。
-
成员变量名:成员变量名只要是一个合法的标识符即可,但这只是从语法角度来说的;如果从程序可读性角度来看,成员变量名应该由一个或多个有意义的单词连缀而成,第一个单词首字母小写,后面每个单词首字母大写,其他字母全部小写,单词与单词之间不要使用任何分隔符。成员变量用于描述类或对象包含的状态数据,因此成员变量名建议使用英文名词。
-
默认值:定义成员变量还可以指定一个可选的默认值。
在创建类的对象时,对类中的所有成员变量都要初始化,赋值过程比较麻烦。如果在对象最初被创建时就完成对其成员变量的初始化,程序将更加简洁。Java 允许对象在创建时进行初始化,初始化是通过构造方法来完成的。在创建类的对象时,使用 new关键字和一个与类名相同的方法来完成,该方法在实例化过程中被调用,称为构造方法。构造方法是一种特殊的成员方法,有以下几个主要特点:
-
构造方法的名称必须与类的名称完全相同。
-
构造方法不返回任何数据,也不需要使用 void 关键字声明。
-
构造方法的作用是创建对象并初始化成员变量。
-
在创建对象时,系统会自动调用类的构造方法。
-
构造方法一般用 public 关键字声明。
-
每个类至少有一个构造方法。如果不定义构造方法,Java 将提供一个默认的不带参数且方法体为空的构造方法。
-
构造方法也可以重载。
代码:
class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
void speak() {
System.out.println("姓名:" + name + ",年龄:" + age);
}
}
public class structureObject_Test {
public static void main(String[] args) {
Person p = new Person("张玉瑞",20);
p.speak();
}
}
构造方法和方法的区别:
-
构造器声明在类的内部。
-
构造方法名字和类名必须一样。
-
构造方法不能有返回类型。
-
构造方法可以包含参数。
Java语言中使用new操作符调用构造方法就可以创建一个对象,创建对象需要3步:
-
声明。声明一个对象,包括对象名称和对象类型。
-
实例化。使用关键字 new 来创建一个对象。
-
初始化。使用 new 创建对象时,会调用构造方法初始化对象。
对象是对类的实例化。在 Java 的世界里“一切皆为对象”,面向对象的核心就是对象。由类产生对象的格式如下:
类名对象名= new 类名();
例如,声明一个对象再实例化
Person pl = new Person();
此代码中,BOOK book = new BOOK()中的book包含BOOK的一引用, 正真的对象为new BOOK(),无论是直接调用getName方法还是引用book调用方法输出值都是一样的。
对象的比较两种比较方式“==”运算符与equals()方法
equals()方法用于比较两个对象引用所指的内容是否相等
==运算符比较的是两个对象引用的地址是否相等
代码:
public class Demo{
public static void main(String[] args){
//创建两个String型对象引用
String c1 = new String("abc");
String c2 = new String("abc");
//将c1对象引用赋予c3
String c3 = c1;
//使用“==”运算符比较c2与c3
System.out.println("c2=c3的运算结果为:"+(c2==c3));
//使用 equals()方法比较c2与c3
System.out.println("c2. equals(c3)的运算结果为:"+(c2.equals(3)));
}
对象的销毁
-
对象超过作用范围将消亡
-
当对象被置null值时将消亡
public static void main(String[] args){
//创建两个String型对象引用
String c1 = new String("abc");
c1=null;
}
二、this关键字
三、方法
定义方法
访问修饰符 返回值类型 方法名(参数列表){
方法体
return 返回值
}
-
访问修饰符:方法允许被访问的权限范围, 可以是 public、protected、private 甚至可以省略 ,其中 public 表示该方法可以被其他任何代码调用
-
返回值类型:方法返回值的类型,如果方法不返回任何值,则返回值类型指定为 void ;如果方法具有返回值,则需要指定返回值的类型,并且在方法体中使用 return 语句返回值
-
方法名:定义的方法的名字,必须使用合法的标识符
-
参数列表:传递给方法的参数列表,参数可以有多个,多个参数间以逗号隔开,每个参数由参数类型和参数名组成,以空格隔开
需要注意以下语法要求:
-
一个方法必须声明返回值类型(构造方法除外)。若方法不返回任何结果,则需要声明返回值类型为void
-
方法在声明时必须指定返回值的类型。若方法不需要返回数据,将返回值类型声明为void;若方法需要返回数据,将返回值类型声明为特定数据类型,可通过 return语句返回, return语句的作用在于结束方法且将数据返回给调用方。
-
若想在main方法中直接调用A方法,则A方法必 statico须声明为
-
方法的参数是指,在调用时传递给方法需要被方法处理的数据。方法可有参数,也可以没有参数,有参数可使方法处理更加灵活。在定义方法时,需要声明该方法所需要的参数变量。在方法调用时,会将实际的参数值传递给方法的参数变量,必须保证传递参数的类型和个数符合方法的声明。
需要注意以下语法现象:
-
方法被调用时,传给被调用方法的实参类型需要和方法定义的形参类型匹配
-
方法调用语句所处的上下文环境要和方法定义的返回值类型匹配
-
如果在主方法中直接调用自定义方法,方法需要关键字static修饰
静态方法 直接使用 类名.方法名 调用
public class Demo { //创建类
public void staticMethods(){
int a = 1;
System.out.println(a);
}
public static void main(String[] args){ //主方法
Demo demo = new Demo();
demo.staticMethods();
}
}
普通方法调用
普通方法 通过new创建对象 然后使用 对象.方法 调用
public class Demo { //创建类
public static void main(String[] args) { //主方法
Demo demo = new Demo();
demo.ordinaryMethods();
}
public void ordinaryMethods() {
int a = 1;
System.out.println(a);
}
}
无参无返回值方法
//创建对象,通过调用该对象的run()方法输出信息
public class Demo {
public static void main(String[] args){ //main方法
Demo Hello = new Demo(); //创建对象,对象名为hello
Hello.run(); //通过对象名.方法名()的形式调用方法
}
public void run(){
System.out.println("积极偶记");
}
}
无参带返回值方法
import java.util.Scanner;
public class Demo {
public static void main(String[] args){
Demo Hello = new Demo(); //创建对象
int avg = Hello.averAge(); //调用方法并将接收方法的返回值,保存在变量avg中
System.out.println("数平均值是:"+avg);
}
public int averAge(){
Scanner input = new Scanner(System.in);
System.out.println("请输入第一个数:");
int a = input.nextInt();
System.out.println("请输入第二个数:");
int b = input.nextInt();
System.out.println("请输入第三个数:");
int c = input.nextInt();
System.out.println("请输入第四个数:");
int d = input.nextInt();
System.out.println("请输入第五个数:");
int e = input.nextInt();
int sum = a+b+c+d+e;
int avg = sum/5;
return avg;
}
}
带参无返回值方法
调用带参方法与调用无参方法的语法形式,但是在调用时必须传入实际的参数值
对象名.方法名(实参1、实参2、.......) smk hello = new smk(); hello.sort(scores);
一定不可忽视的问题:
-
调用带参方法时,必须保证实参的数量、类型、顺序与形参一一对应调用方法时,实参不需要指定数据类型,如 Hello.show("World");
-
方法的参数可以是基本数据类型,如 int、double 等,也可以是引用数据类型,如 String、数组等
-
当方法参数有多个时,多个参数间以逗号分隔
public class Demo { //申明一个名为smk的包
public static void main(String[] args){ //公共的,静态的,无返回值的,函数名为main的,参数为String类型
Demo hello = new Demo(); //创建对象,对象名为hello
hello.show(180,175); //调用方法,传入两人身高
}
public void show(int a,int b){ //定义有参无返回值方法
double avg = (a+b)/2;
System.out.println("两人平均身高是:"+avg);
}
}
带参带返回值方法
通过定义带参带返回值的方法来实现:创建指定长度的 int 型数组,并生成指定范围以内随机数为数组中的每个元素赋值,然后排序输出数组。
import java.util.Scanner;
import java.util.Arrays;
public class Demo {
public static void main(String[] args){
Demo hello = new Demo();
Scanner input = new Scanner(System.in);
System.out.println("请输入数组长度:");
int n = input.nextInt();
int[] scores = hello.show(n); //调用方法并将数值保存在变量n中
System.out.print(Arrays.toString(scores)); //将数组转换成字符串再输出
}
public int[] show(int n){
int[] scores = new int[n]; //定义指定长度的整数类型
Scanner input = new Scanner(System.in);
System.out.println("请输入整数型随机数范围:");
int j = input.nextInt();
for(int i = 0 ; i < scores.length ; i++){
scores[i] = (int)(Math.random() *j); //(int)(Math.random() * j) 生成 j 以内的随机数
}
Arrays.sort(scores); //将数组排序
return scores; //返回赋值后的数组
}
}
四、方法重载
方法重载
Java 允许同一个类里定义多个同名方法,只要形参列表不同就行。如果同一个类中包含了两个或两个以上方法的方法名相同,但形参列表不同,则被称为方法重载。在 Java 程序中确定一个方法需要三个要素:
-
调用者,也就是方法的所属者,既可以是类,也可以是对象。
-
方法名,方法的标识。
-
形参列表,当调用方法时,系统将会根据传入的实参列表匹配。
方法的重写要遵循“两同两小一大”规则,“两同”即方法名相同、形参列表相同;“两小”指的是子类方法返回值类型应比父类方法返回值类型更小或相等,子类方法声明抛出的异常类应比父类方法声明抛出的异常类更小或相等;“一大”指的是子类方法的访问权限应比父类方法的访问权限更大或相等。尤其需要指出的是,覆盖方法和被覆盖方法要么都是类方法,要么都是实例方法,不能一个是类方法,一个是实例方法。
下例代码中重写了fly()方法,并创建了子类方法对象,执行main()方法时执行的不再是父类的fly()方法,而是子类的fly()方法,即子类重写或覆盖了父类的方法。
public class Brid {
public void fly(){
System.out.println("I can fly");
}
}
public class Ostrich extends Brid{
public void fly(){
System.out.println("I can only run on the ground");
}
public static void main(String[] args) {
Ostrich o = new Ostrich();
o.fly();
}
}
如果需要在子类方法中调用父类中被覆盖的方法,则可使用super关键字或者父类类名作为调用者来调用父类中被覆盖的方法
以父类类名作为调用者:
public static void main(String[] args) {
Brid b = new Brid();
b.fly();
}
super关键字
通过super关键字来实现对父类成员的访问,用来引用当前对象的父类
-
子类的构造过程中必须调用其父类的构造方法
-
如果子类的构造方法中没有显示调用父类的构造方法,则系统默认调用父类无参的构造方法
-
如果显示的调用构造方法,必须在子类的构造方法第一行
-
如果子类构造方法中既没有显示调用父类的构造方法,而父类又没有无参的构造方法,则编译出错
public void fly(){
super.fly();
}
五、作用修饰符
| 访问修饰符 | 同一个类中 | 同一个包中 | 不同包中的子类 | 不同包中的非子类 |
| private | √ | |||
| 无访问修饰符 | √ | √ | ||
| protected | √ | √ | √ | |
| public | √ | √ | √ | √ |
private 修饰符对应最严格的访问级别,被声明为 private 的方法、变量和构造方法只能被所属类访问,并且类和接口不能声明为 private。声明为私有访问类型的变量只能通过类中的公共方法被外部类访问。private 修饰符主要用来隐藏类的实现细节和保护类的数据。
public class PrivateTest{
private String name; //私有的成员变量
private String getName(){ //私有成员变量的get方法
return name;
}
public void setName(String name) { //私有成员变量的set方法
this.name = name;
}
public static void main(String[] args) {
PrivateTest p = new PrivateTest();
p.setName("private访问修饰符");
System.out.println("name="+p.getName());
}
}
无访问修饰符
不使用访问修饰符声明的变量和方法,可以被这个类本身或者与类在同一个包内的其他类访问。接口中的变量都隐式声明为 public static final,而接口中的方法默认情况下访问权限为 public,因此无访问修饰符的情况也称为默认访问修饰符。
public class DefaultTest{
String name;
String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public static void main(String[] args) {
DefaultTest dt = new DefaultTest();
dt.setName("default test");
System.out.println(dt.getName());
}
}
受保护的访问修饰符 protected
protected 修饰符不能修饰类和接口,方法和成员变量能够声明为 protected,但是接口的成员变量和成员方法不能声明为 protected。
public class Person {
protected String name;
protected void sing() {
System.out.println("父类……");
}
}
public class SonPerson extends Person {
public static void main(String[] args) {
SonPerson sp = new SonPerson();
sp.sing();
sp.name = "protected";
System.out.println(sp.name);
}
}
公有的访问修饰符 public
被声明为 public 的类、方法、构造方法和接口能够被任何其他类访问。如果几个相互访问的 public 类分布在不同的包中,则需要用关键字 import 导入相应的 public 类所在的包。由于类的继承性,类所有的公有方法和变量都能被其子类继承。
public class Person {
public void test() {
System.out.println("父类:public test");
}
}
public class publicTest {
public static void main(String[] args) {
Person sp = new Person();
sp.test();
}
}
非访问修饰符
(1)、static 修饰符
static 修饰符用来修饰类的成员变量和成员方法,也可以形成静态代码块。static 修饰的成员变量和成员方法一般称为静态变量和静态方法,可以直接通过类名访问它们。访问的语法格式一般为
类名.静态方法名(参数列表); 类名.静态变量名;
用 static 修饰的代码块表示静态代码块,当 Java 虚拟机(JVM)加载类时,就会执行该代码块。
static关键字可修饰属性、方法、内部类、代码块
static方法一般称作静态方法,由于静态方法不依赖于任何对象就可以进行访问,因此对于静态方法来说,是没有this的,因为它不依附于任何对象,既然都没有对象,就谈不上this了。并且由于这个特性,在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用。
但是要注意的是,虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法/变量的
static关键字的作用:
-
修饰成员变量和成员方法
-
静态代码块
-
修饰类(只能修饰内部类)
-
静态导包
1、使用类名调用静态方法
public class Text {
public static void main(String[] args) {
Text.add(); //通过类名.方法名调用方法
}
public static void add() {
int a = 3;
System.out.println("直接使用类名调用静态方法:"+a);
}
}
2、使用对象名调用静态方法
public class Text {
public static void main(String[] args) {
Text text = new Text(); //创建对象text
text.add(); //通过类名.方法名调用方法
}
public static void add() {
int a = 3;
System.out.println("使用对象名调用静态方法:"+a);
}
}
3、静态方法中可以直接调用同类中的静态成员,但不能直接调用非静态成员
public class Text {
String Name = "abc"; //非静态变量
static String NameDemo = "xyz"; //静态变量
public static void Demo() {
System.out.println("访问非静态变量:" + Name); //不能直接调用非静态成员
System.out.println("访问静态变量:" + NameDemo);
}
4、如果希望在静态方法中调用非静态变量可以通过创建类的对象,然后通过对象来访问非静态变量
public class Text {
String Name = "abc"; //非静态变量
static String NameDemo = "xyz"; //静态变量
public static void Demo() {
Text text = new Text(); //创建对象text
System.out.println("访问非静态变量:" + text.Name); //通过创建的对象text来访问非静态变量
System.out.println("访问静态变量:" + NameDemo);
}
5在普通成员方法中,可以直接访问同类的非静态变量和静态变量
public class Text {
String Name = "abc"; //非静态变量
static String NameDemo = "xyz"; //静态变量
public void Demo() {
System.out.println("访问非静态变量:" + Name);
System.out.println("访问静态变量:" + NameDemo);
}
public static void main(String[] args) {
sonText son = new sonText();
son.Demo();
}
}
public class sonText extends Text{
//重写方法
public static void main(String[] args) {
sonText son = new sonText();
son.Demo();
}
}、
(2)final 修饰符
final 可以修饰类、方法和变量,意义不同,但是本质相同,都是表示不可改变。
1、修饰类,表示该类无法被继承
在父类前加载final关键字,则子类不能继承父类
package Demo;
final public class Father {
public void way(){ //定义无参无返回值构造方法
System.out.println("子类son继承了Father父类的方法");
}
}
package Demo;
public class Son extends Father{
public String name = "name";
public void way(){
//重写父类方法
System.out.println("子类son继承了Father父类的方法");
}
}
2、修饰方法,表示该方法无法被重写
在父类means()方法中加载final关键字,则在子类中父类方法不能被重写
public class Father {
public void way(){ //定义无参无返回值构造方法
System.out.println("子类son继承了Father父类的方法");
}
public String name = "name";
final public void means(){
System.out.println(name);
}
}
package Demo;
public class Son extends Father{
public String name = "name";
public void means(){
System.out.println(name);
}
}
3、修改属性 属性的值只能修改一次
如果final关键字修饰了属性,那么再次修改方法时会报错
public class Father {
final public String name = "koko" ;
public void way(){ //定义无参无返回值构造方法
System.out.println("子类son继承了Father父类的方法");
}
public means(){
System.out.println(name);
String name = "在父类中修改名字";
}
}
package Demo;
public class Son extends Father{
public void Way(){ //定义无参无返回值构造方法
System.out.println("子类son继承了Father父类的方法");
}
public means(){
System.out.println(name);
String name = "在子类中修改名字";
}
}
package Demo;
public class Text {
public static void main(String[] args) {
Son s = new Son();
s.means("main函数中不能修改属性值");
}
}
4、修饰变量,该变量的值只能赋一次值,即变为常量
在父类属性前添加了final关键字,那么就不允许再次修改属性值
public class Father {
public int age = 20;
public void way(){ //定义无参无返回值构造方法
System.out.println("子类son继承了Father父类的方法");
}
public Father(){
final int a = 2; //常量
a=8;
System.out.println(a);
}
}
(3)、abstract 修饰符
abstract 用来修饰类,这个类称为抽象类。抽象类不能用来实例化对象,声明抽象类的唯一目的是为了是将来对该类进行扩充。
抽象类可以包含抽象方法和非抽象方法。如果一个类包含若干个抽象方法,那么该类必须声明为抽象类。
抽象类可以不包含抽象方法。抽象方法的声明以分号结尾。
抽象方法不能被声明成 final 和 static。抽象方法是一种没有任何实现的方法,该方法的具体实现由子类提供。任何继承抽象类的子类必须实现父类的所有抽象方法,除非该子类也是抽象类。
(4)、synchronized 修饰符synchronized 声明的方法同一时间只能被一个线程访问。synchronized 的作用范围有如下两种:
-
在某个对象内,synchronized 修饰的方法可以防止多个线程同时访问。这时,不同的对象的synchronized 方法是不相干扰的。也就是说,其他线程照样可以同时访问相同类的另一个对象中的synchronized 方法。如果一个对象有多个 synchronized 方法,只要一个线程访问了其中的一个 synchronized方法,其他线程就不能同时访问这个对象中任何一个 synchronized 方法。
-
在某个类中,用 synchronized 修饰
序列化的对象包含被 transient 修饰的成员变量时,JVM 跳过该特定的变量。该修饰符包含在定义变量的语句中,用来预处理类和变量的数据类型。
(6)、volatile 修饰符Java 语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了同步块和 volatile 关键字机制。volatile 修饰的成员变量在每次被线程访问时都强制从共享内存中重新读取该成员变量的值。而且当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。一个 volatile 对象引用可能是 null。
六、成员变量和局部变量 成员变量成员变量指的是在类里定义的变量。成员变量被分为类变量和实例变量两种,定义成员变量时没有 static 修饰的就是实例变量,有 static 修饰的就是类变量。其中类变量从该类的准备阶段起开始存在,直到系统完全销毁这个类,类变量的作用域与这个类的生存范围相同;而实例变量则从该类的实例被创建起开始存在,直到系统完全销毁这个实例,实例变量的作用域与对应实例的生存范围相同。
class Person
{
//定义实例变量
public String name;
//定义类变量
public static int eyeNum;
}
public class PersonTest
{
public static void main(String[] args) {
//第一次主动使用person类,该类自定初始化,则eyeNum变量开始起作用,输出0
System.out.println("PersonTest的eyeNum类变量值:"+Person.eyeNum);
Person p = new Person();
System.out.println("p变量的name变量值是:"+p.name+"p对象的eyeNum变量值是:"+p.eyeNum);
p.name = "张三";
p.eyeNum = 2;
System.out.println("p变量的name变量值是:"+p.name+"p对象的eyeNum变量值是:"+p.eyeNum);
System.out.println("Person的eyeNum类变量值:"+Person.eyeNum);
Person p2 = new Person();
System.out.println("p2对象的eyeNum类变量值:"+p2.eyeNum);
}
}
局部变量
局部变量指的是在方法里定义的变量,局部变量根据定义形式的不同,又可以被分为如下三种:
-
形参:在定义方法签名时定义的变量,形参的作用域在整个方法内有效。
-
方法局部变量:在方法体内定义的局部变量,它的作用域是从定义该变量的地方生效,到该方法结束时失效。
-
代码块局部变量:在代码块中定义的局部变量,这个局部变量的作用域从定义该变量的地方生效,到该代码块结束时失效。
与成员变量不同的是,局部变量除了形参之外,都必须显式初始化。也就是说,必须先给方法局部变量和代码块局部变量指定初始值,否则不可以访问它们。
定义代码块局部变量,可以看出,只要离开了定义代码块局部变量所在的代码块,这个局部变量就立刻被销毁,变为不可见,所以其作用域只在定义代码块局部变量所在的代码块中,而方法局部变量则从定义该变量开始,直到该方法结束。
定义代码块局部变量
public class BlockTest{
public static void main(String[] args) {
{
//定义一个代码块局部变量
int a;
//为变量a初始化
a = 5;
System.out.println("代码块局部变量a的值:"+a);
}
System.out.println(a);
}
}
定义方法局部变量
public class BlockTest{
public static void main(String[] args) {
//定义一个代码块局部变量
int a;
//为变量a初始化
a = 5;
System.out.println("方法局部变量a的值:"+a);
}
}
七、封装
封装
将类的某些信息隐藏在内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏的信息的操作和访问
使用包管理Java中的类:
package Testphone;
作用:
-
管理Java文件
-
解决同名文件冲突
定义包:
package 包名;
必须放在Java程序的第一行,包间可以用“.”号隔开
使用:
-
可以通过关键字,在某个文件使用其他文件中的类
-
java中,包的命名规范是全小写字母拼音
-
使用的时候不但可以加在某个包下的所有文件,也可以加载某个具体子包下的所有文件
封装的优点如下:
-
良好的封装能够减少耦合
-
类内部的结构可以自由修改。
-
可以对成员变量进行更精确的控制。
-
隐藏信息,实现细节。
class person {
private String Name;
private int age;
private float weight;
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 float getWeight() {
return weight;
}
public void setWeight(float weight) {
this.weight = weight;
}
}
public class Test{
public static void main(String[] args) {
person pt = new person();
pt.setName("张三");
pt.setAge(18);
pt.setWeight(80);
System.out.println(pt.getName());
System.out.println(pt.getAge());
System.out.println(pt.getWeight());
}
}
内部类
定义在另一个类里面的类(内部类可以是静态(static)的,可以使用 public、protected 和 private 访问控制符)
作用·:
-
内部类提供了更好的封装,可以把隐藏在外部类之内,不允许同一个包中的其他类访问该类
-
内部类的方法可以直接访问外部类的所有数据,包括私有的数据
-
内部类所实现的功能使用外部类同样可以实现
可分为两种:
1、类中内部类
-
私有内部类
-
静态内部类(普通内部类之前加了一个static)
2、方法内内部类
-
局部内部类
-
匿名内部类(没有名字的类,在某个类内部使用,因为没有名字,别的类将无法调用它,只能在创建的时候,使用new语句声明它们)
内部类的使用方法:
-
Inner类位于HelloWorld类的内部,相当于HelloWorld的一个成员变量的位置,Inner可以使用任意访问控制符,如:public、protected、private等
-
Inner类中定义的show()方法可以直接访问HelloWorld类中的数据,而不受访问控制符的影响,如直接访问HelloWorld类中的私有属性a
-
定义了成员内部类后,必须使用外部类对象来创建内部类对象,而不能直接去new一个内部类对象,即:内部类 对象名 = 外部类对象 . new 内部类();
-
会产生两个class文件
外部类是不能直接使用内部类的成员和方法,可先创建内部类的对象,然后通过内部类的对象来访问其成员变量和方法
-
如果外部类和内部类具有相同的成员变量或方法,内部类默认访问自己的成员变量或方法,如果要访问外部类的成员变量,可以使用 this 关键字
public class Text{ //外部类Text
public class Inner{ //内部类Inner,类Inner在类HelloWorld
public void show(){ //内部类的方法
System.out.println("内部类的方法");
}
}
public static void main(String[] args) { //创建main方法
Text hello = new Text(); //创建外部类对象
Inner i = hello.new Inner();
i.show(); //调用内部类对象的方法
}
}
成员内部类
使用内部类中定义的非静态变量和方法时,要先创建外部类的对象,再由“outObjectName.new”操作符创建内部类的对象,再调用内部类的方法
public class Text { //外部类Text
private int a = 7; //外部类的私有属性
public class Inner{ //内部类Inner
int b = 9; //内部类的成员属性
public void show(){ //内部类方法
System.out.println("访问内部类中的a:"+a);
System.out.println("访问内部类中的b:"+b);
}
}
public static void main(String[] args) { //创建main方法
Text hello = new Text(); //创建外部类对象hello
Inner i = hello.new Inner();
i.show(); //调用内部类对象的方法
}
}
静态内部类
特点:
-
静态内部类不能直接访问外部类的非静态成员,但可以通过new 外部类(). 成员的方式访问
-
如果外部类的静态成员与内部类的成员名称相同,可通过“类名.静态成员”访问外部类的静态成员;如果外部类的静态成员与内部类的成员名称不相同,则可通过“成员名”直接调用外部类的静态成员
-
创建静态类内部的对象时,不需要外部类的对象,可以直接创建 内部类 对象名 = new 内部类()
public class Text { //外部类Text
private int a = 7; //外部类的私有属性
public class Inner{ //内部类Inner
int b = 9; //内部类的成员属性
public void show(){ //内部类方法
System.out.println("访问内部类中的a:"+a);
System.out.println("访问内部类中的b:"+b);
}
}
public static void main(String[] args) { //创建main方法
Text hello = new Text(); //创建外部类对象hello
Inner i = hello.new Inner();
i.show(); //调用内部类对象的方法
}
}
局部内部类
特点:
-
仅在定义了它们的代码块中是可见的
可以使用定义他们的代码块中的任何局部final变量
-
局部类不可以是static的,里面不能定义static成员
-
局部类不可以用public、private、protected修饰,只能使用缺省的
-
局部类可以是abstract的
public class Text { //外部类Text
int num1 = 2; //定义常量num1
int num2 = 4; //定义常量num2
public void num(){ //外部类中的num方法
class Inner{ //内部类
int num3 = 6; //定义常量num3
public int innTest(){ //内部类中的innTest方法
System.out.println("外部类num1="+num1);
System.out.println("内部类num2="+num2);
return num3; //返回num3的值
}
}
Inner inn = new Inner(); //创建方法内部类的对象
int num3 = inn.innTest(); //调用内部类的方法
System.out.println("内部类num3="+num3);
}
public static void main(String[] args) { //main方法
Text inn = new Text(); //创建外部类的对象
inn.num(); //调用外部类的对象
}
}
匿名内部类
匿名内部类也就是没有名字的内部类正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写,但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口,但最多只能继承一个父类,或实现一个接口。
关于匿名内部类还有如下两条规则:
-
匿名内部类不能是抽象类,因为系统在创建匿名内部类的时候,会立即创建内部类的对象。因此不允许将匿名内部类定义成抽象类。
-
匿名内部类不等定义构造器(构造方法),因为匿名内部类没有类名,所以无法定义构造器,但匿名内部类可以定义实例初始化块
特点:
-
子类拥有父类非private的属性,方法。
-
子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
-
子类可以用自己的方式实现父类的方法。
-
Java的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如A类继承B类,B类继承C类,所以按照关系就是C类是B类的父类,B类是A类的父类,这是java继承区别于C++继承的一个特性。
-
提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系)。
语法:
class 子类 extends 父类{
....
}
实例
public class Animal { //定义父类
public int age; //定义属性
public String name;
public void eat(){ //父类方法
System.out.println("年龄:"+age);
System.out.println("名字:"+name);
System.out.println("动物具有吃东西的能力");
}
}
public class Dog extends Animal { //通过extends关键字将Dog和Animal建立联系
}
public class test {
public static void main(String[] args) {
Dog dog = new Dog(); //创建Dog对象
dog.age = 45; //调用属性和方法
dog.name = "哈士奇";
dog.eat();
}
}
为了保证父类有良好的封装性,不会被子类随意改变,设计父类通常应该遵循如下规则。
-
尽量把父类的所有成员变量都设置成 private 访问类型,不要让子类直接访问父类的成员变量,尽量隐藏父类的内部数据。。
-
不要让子类可以随意访问、修改父类的方法。父类中那些仅为辅助其他的工具方法,应该使用private 访问控制符修饰,让子类无法访问该方法;如果父类中的方法需要被外部类调用,则必须以 public 修饰,但又不希望子类重写该方法,可以使用 final 修饰符(该修饰符后面会有更详细的介绍)来修饰该方法;如果希望父类的某个方法被子类重写,但不希望被其他类自由访问,则可以使用 protected 来修饰该方法。
-
尽量不要在父类构造器中调用将要被子类重写的方法。
-
父类静态代码区和父类静态成员
-
子类静态代码区和子类静态成员
-
父类非静态代码区和普通成员
-
父类构造函数
-
子类非静态代码区和普通成员
-
子类构造函数
代码:
public class Bus {
public int site = 75;
public int numbers;
public void print(){
System.out.println("the Bus have " + site +"个座位,n" + numbers + "位乘客");
}
public Bus(){
System.out.println("Bus类执行了");
site = 89;
}
}
public class car extends Bus {
public car() {
System.out.println("Car类执行了");
}
}
public class Test {
public static void main(String[] args) {
Bus bu = new Bus();
System.out.println("bus site:"+bu.site);
car bus = new car();
bus.site = 78;
bus.numbers = 59;
bus.print();
}
}
九、多态
多态
Java 引用变量有两个类型:一个是编译时类型,一个是运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。如果编译时类型和运行时类型不一致,就可能出现所谓的多态(Polymorphism)。
一个对象可以有多种形态,既可以当作本类的对象来看,也可以当作其父类对象来看。我们可以把一个对象当作它的父类对象来看,也就是定义父类的引用得到子类的对象,如下:
Person a = new Student;引用类型转换
向上转型(强制类型转换),是大类型到小类型,父类引用指向子类对象
public class animal {
Dog dog = new Dog();
animal an = new dog();
}
//另一种方式
public class animal {
animal an = new dog();
}
向下转型(隐式/自动类型转换),是小类型到大类型的转换,
public class animal {
animal an = new animal();
Dog dog = (Dog)an;
}
instanceof操作符
instanceof 用于判断前面的对象是否是后面的类,或者其子类、实现类的实例。如果是,则返回 true,否则返回 false。
instanceof运算符的前一个操作数通常是一个引用类型变量,后一个操作数通常是一个类(也可以是接口,可以把接口理解成一种特殊的类)。
在使用 instanceof 运算符时需要注意:instanceof 运算符前面操作数的编译时类型要么与后面的类相同,要么与后面的类具有父子继承关系,否则会引起编译错误。下面程序示范了 instanceof 运算符的用法。
public static void main(String[] args) {
animal an = new Dog();
if(an instanceof animal){
Dog dog2 = (Dog)an;
}else{
System.out.println("错误");
}
}
十、初始化块
初始化块
初始化块的修饰符只能是static,否则就是普通初始化块,使用static的初始化代码块称为静态初始化块
语法:
[修饰符] {
//初始化块的可执行代码
……
}
下面程序定义两个初始化块,一个构造器
public class Person {
//定义初始化代码块
{
int a = 6;
if(a>4)
System.out.println("Person初始化块:局部变量a的值大于4");
System.out.println("Person的初始化块");
}
//定义第二个初始化块
{
System.out.println("Person的第二个初始化块");
}
public Person(){
System.out.println("Person类的无参数构造器");
}
public static void main(String[] args) {
new Person();
}
}
输出:
Person初始化块:局部变量a的值大于4
Person的初始化块
Person的第二个初始化块
Person类的无参数构造器
静态初始化块
如果定义初始化块时使用了 static 修饰符,则这个初始化块就变成了静态初始化块,也被称为类初始化块(普通初始化块负责对对象执行初始化,类初始化块则负责对类进行初始化)。静态初始化块是类相关的,系统将在类初始化阶段执行静态初始化块,而不是在创建对象时才执行。因此静态初始化块总是比普通初始化块先执行。
public class Root {
static{
System.out.println("Root的静态初始化代码块");
}
{
System.out.println("Root的普通初始化代码块");
}
public Root()
{
System.out.println("Root的无参数的构造器");
}
}
public class Mid extends Root{
static{
System.out.println("Mid的静态初始化代码块");
}
{
System.out.println("Mid的普通初始化代码块");
}
public Mid()
{
System.out.println("Mid的无参数的构造器");
}
public Mid(String msg){
System.out.println("Mid的带参构造器,其参数值:"+msg);
}
}
public class Leaf extends Mid{
static{
System.out.println("Leaf的静态初始化代码块");
}
{
System.out.println("Leaf的普通初始化代码块");
}
public Leaf()
{
super("姓名:张三");
System.out.println("执行Leaf的构造器");
}
}
public class Test{
public static void main(String[] args) {
new Leaf();
new Leaf();
}
}
输出:
Root的静态初始化代码块
Mid的静态初始化代码块
Leaf的静态初始化代码块
Root的普通初始化代码块
Root的无参数的构造器
Mid的普通初始化代码块
Mid的带参构造器,其参数值:姓名:张三
Leaf的普通初始化代码块
执行Leaf的构造器
Root的普通初始化代码块
Root的无参数的构造器
Mid的普通初始化代码块
Mid的带参构造器,其参数值:姓名:张三
Leaf的普通初始化代码块
执行Leaf的构造器



