147
社区成员




信管1221 202211671126
一
知识点
二进制的原码、反码和补码
原码就是二进制定点表示法,即最高位为符号位,“0”表示正,“1”表示负,其余位表示数值的大小。
反码表示法规定:正数的反码与其原码相同;负数的反码是对其原码逐位取反,但符号位除外。
补码表示法规定:正数的补码与其原码相同;负数的补码是在其反码的末位加1。
首位表示符号位,原码、反码和补码都是本身。
如何计算负数在计算机中的二进制(即它的补码)?
首先对负数的绝对值取反,然后+1得到负数的补码。
例如:byte a=-5
-5的绝对值=5 5的二进制表示是 0000 0101 取反得到 1111 1010 +1运算得到 1111 1011 ,那么1111 1011就是-5的补码。
也可以这样,用原码计算
例如:byte a=-5 原码表示 1000 0101
计算其反码为 1111 1010
计算其补码为 1111 1011
所以 -5在计算机中表示为1111 1011
byte的取值范围为什么是-128~127
byte 表示一个字节,8bit,其中最高位是符号位。因此,最大值为 0111 1111,即127;最小值是负数为 1000 0000(规定)【其他数据类型的最大最小值也可以这样以此类推】。在计算机中二进制都是以补码的形式表示的,我们也可以这样来理解,为什么最小是-128
最小负数补码表示为:1000 0000
由补码计算原码:
1000 0000 作-1 运算 得到 0111 1111,取反 1000 0000 因为负数的补码是其绝对值取反,即10000000为最小负数的绝对值,而10000000的十进制表示是128,所以最小负数是-128
标准ASCII 码也叫基础ASCII码,使用7 位二进制数(剩下的1位二进制为0)来表示所有的大写和小写字母,数字0 到9、标点符号, 以及在美式英语中使用的特殊控制字符。
ASCII码表示的范围是0~127 刚好byte类型可以覆盖到。因此byte类型和ASCII可以互相转换。可以根据ASCII对照表进行查看http://tool.oschina.net/commons?type=4
当byte类型的变量的值实时是在''单引号里面的时候,该byte变量的值表示的是ASCII码,可以通过ASCII对照表查看其真实的数值。
byte->int 因为byte是8位,而int是32位,当byte类型转换为int类型的时候,会自动用符号位补足高位。例如 -5 1111 1011 转为int 类型会表示为 1111 1111 1111 1111 1111 1111 1111 1011 其表示的十进制的数值不变。
有的时候,我们只想保留二进制的补码形式,我们可以用byte&0xff。0xff是十六进制,默认是int类型,其二进制表示为 0000 0000 0000 0000 0000 0000 1111 1111 将byte类型的数值跟0xff作&操作,可以将高24位变为0,保留低8位。当然对应的十进制数也发生了变化。(相当于将byte变成无符号int类型)
例如:int a=234 byte b=(byte)a -->得到b=-22 因为234已经超出了byte类型的表示范围,强转会直接丢弃高位,保留低8位。
int类型的234的二进制表示为:1110 1010【高24位全是0】 强转为byte类型,舍弃高24位得到1110 1010 ,第一位为符号位1,得到一个负数-22。
–boolean 布尔
–byte 字节
–short/int/long 短整数/整数/长整数
–float/double 浮点数
–char 字符
– 只有true,或者false两种值,默认是false
–byte 字节, 1 byte = 8 bits (8位)
–存储有符号的,以二进制补码表示的整数
–最小值-128,最大值127,默认值0
–byte 类型用在大型数组中可以显著节约空间,主要代替小整数,因为 byte 变量占用的空间只有 int类型的四分之一
–byte在二进制文件读写中使用较多
short,16位,2个字节,有符号的以二进制补码表示的整数
–(-32768~32767, -2^15~2^15-1),默认值0
• int, 32位,4个字节,有符号的以二进制补码表示的整数
–(-2147483648~2147483647, -2^31~2^31-1),默认值0
• long, 64位,8个字节,有符号的以二进制补码表示的整数
–-9,223,372,036,854,775,808(-2^63)~
9,223,372,036,854,775,807(2^63 -1),默认值0L
• float,单精度,32位,4个字节,符合IEEE 754标准的浮点
数,默认值0.0f。float的范围为1.40129846432481707e-45 ~
3.40282346638528860e+38 (无论正负)。
• double,双精度,64位,8个字节,符合IEEE 754标准的浮
点数,默认值0.0d。double的范围为4.94065645841246544e324d ~ 1.79769313486231570e+308d (无论正负) 。
• float和double都不能用来表示很精确的数字。
• char是一个单一的 16 位 Unicode 字符
• 最小值是 \u0000(即为0);
• 最大值是 \uffff(即为65,535);
• char 数据类型可以储存任何字符;
• +,-,*, /, %
• 逻辑运算符 &&, ||, !
• 比较运算符 !=, >, >=, <, <=, ==
• 移位运算符>>, <<等
• 不用背诵运算符优先级,用括号隔开
注意:整数型数值同整数型数值计算结果仍为整数。如5/2的结果是2。
public class FloatDoubleMaxMinTest {
public static void main(String[] args) {
System.out.println(Float.MIN_VALUE);
System.out.println(Float.MAX_VALUE);
System.out.println(Double.MIN_VALUE);
System.out.println(Double.MAX_VALUE);
float f1 = Float.MIN_VALUE;
float f2 = Float.MAX_VALUE;
float f3 = -1 * Float.MIN_VALUE;
float f4 = -1 * Float.MAX_VALUE;
System.out.println(f1 + ", " + f2 + ", " + f3 + ", " + f4 );
double d1 = Double.MIN_VALUE;
double d2 = Double.MAX_VALUE;
double d3 = -1 * Double.MIN_VALUE;
double d4 = -1 * Double.MAX_VALUE;
System.out.println(d1 + ", " + d2 + ", " + d3 + ", " + d4 );
}
}
理解变量的作用域,局部变量的特点。
public class Part { // 新建类Part
// 声明常量PI,此时如不对PI进行赋值,则会出现错误提示
static final double PI = 3.14;
static int age = 23; // 声明int型变量age并进行赋值
public static void main(String[] args) { // 主方法
final int number; // 声明int型常量number
number = 1235; // 对常量进行赋值
age = 22; // 再次对变量进行赋值
// number = 1236;
// //错误的代码,因为number为常量,只能进行一次赋值
System.out.println("常量PI的值为:" + PI); // 将PI的值输出
System.out.println("赋值后number的值为:" +number);//将number的值输出
System.out.println("int型变量age的值为:" + age); // 将age的值输出
}
}
public class Val { // 新建类
static int times = 3; // 定义成员变量times
public static void main(String[] args) { // 主方法
int times = 4; // 定义局部变量times
System.out.println("times的值为:" + times); // 将times的值输出
}
}
在Java程序中要完成的所有任务都可分解为一系列的语句。在编程语言中,语句是简单命令,它会命令计算机执行某种操作。
语句表示程序中发生的单个操作,接下来先看两条简单的Java语句,具体示例如下:
int i=10;
System.out.println("Hello World!");
如上所示是两条简单的Java语句,还有些语句能够提供一个值,例如将两个数相加生成一个值的语句称为表达式,这个值可以存储下来供程序使用,语句生成的值称为返回值。另外,有些表达式生成数字值,有些表达式生成布尔值等。
Java程序中通常每条语句占一行,但这只是一种格式规范,并不能决定语句到哪里结束,Java语句都以分号(;)结尾,可以在一行写多条语句,具体示例如下。
int i1=10; int i2=20;
如上所示是两条Java语句,但为了让程序便于他人阅读和理解,建议写代码时遵循格式规范,每条语句占一行。
类(class)是Java的基本结构,一个程序可以包含一个或多个类,Java使用class关键字声明一个类,其语法格式如下:
修饰符 class 类名 {
程序代码
}
如上所示为声明一个类的格式,接下来按照这个格式来声明一个类,具体示例如下:
public class HelloWorld { // 声明一个名为HelloWorld的类
}
修饰符(modifier)用于指定数据、方法、类的属性以及用法,具体示例如下:
public class HelloWorld { //public修饰为公有的
public static void main(String[] args) { //static修饰为静态的
}
}
Java中使用左大括号({)和右大括号(})将语句编组,组中的语句称为代码块或块语句,具体示例如下:
{
int i1=10;
int i2=20;
}
如上所示得两条语句在大括号中,称为块语句。
数组是可以保存一组数据的一种数据结构,它本身也会占用一个内存地址,因此数组是引用类型。定义数组的语法格式如下:
数据类型[] 数组名;
对于数组的声明也可用另外一种形式,其语法格式如下:
数据类型 数组名[];
上述两种不同语法格式声明的数组中,“[ ]”是一维数组的标识,从语法格式可见,它既可放置在数组名前面,也可以放在数组名后面。面向对象程序设计更侧重放在前面,保留放在后面是为了迎合C程序员的使用习惯,在这里推荐使用第一种格式。
静态初始化是指由程序员在初始化数组时为数组每个元素赋值,由系统决定数组的长度。
数组的静态初始化有两种方式,具体示例如下:
int[] array; // 声明一个int类型数组
array = new int[]{1,2,3,4,5}; // 静态初始化数组
int[] array = new int[]{1,2,3,4,5}; // 声明并初始化数组
对于数组的静态初始化也可简写,具体示例如下:
int[] array = {1,2,3,4,5}; // 声明并初始化一个int类型的数组
上述示例中静态初始化了数组,其中大括号包含数组元素值,元素值之间用逗号“,”分隔。此处注意,只有在定义数组的同时执行数组初始化才支持使用简化的静态初始化。
动态初始化是指由程序员在初始化数组时指定数组的长度,由系统为数组元素分配初始值。
数组动态初始化的具体示例如下:
int[] array = new int[10]; // 动态初始化数组
上述示例会在数组声明的同时分配一块内存空间供该数组使用,其中数组长度是10,由于每个元素都为int型数据类型,因此上例中数组占用的内存共有10×4=40个字节。此外,动态初始化数组时,其元素会根据它的数据类型被设置为默认的初始值。本例数组中每个元素的默认值为0,其他常见的数据类型默认值如表5.1所示。
成员变量类型 |
初始值 |
成员变量类型 |
初始值 |
byte |
0 |
double |
0.0D |
short |
0 |
char |
空字符,’\u0000’ |
int |
0 |
boolean |
false |
long |
0L |
引用数据类型 |
null |
float |
0.0F |
|
|
在Java中,数组对象有一个length属性,用于表示数组的长度,所有类型的数组都是如此。
获取数组长度的语法格式如下:
数组名.length
数组的遍历是指依次访问数组中的每个元素。
通过前面已经掌握的知识,用数组的基本用法与流程控制语句的使用来得到数组中的最大值和最小值,首先把数组的第一个数赋值给变量max和min,分别表示最大值和最小值,再依次判断数组的其他数值的大小,判断当前值是否最大值或最小值,如果不是则进行替换,最后输出最大值和最小值。
数组排序是指数组元素按照特定的顺序排列。数组排序有多种算法,本节介绍一种简单的排序算法——冒泡排序。这种算法是不断的比较相邻的两个元素,较小的向上冒,较大的向下沉,排序过程如同水中气泡上升,即两两比较相邻元素,反序则交换,直到没有反序的元素为止。
数组是引用数据类型,因此数组变量就是一个引用变量,通常被存储在栈(Stack)内存中。数组初始化后,数组对象被存储在堆(Heap)内存中的连续内存空间,而数组变量存储了数组对象的首地址,指向堆内存中的数组对象。接下来演示一维数组在内存中的存储原理,如图5.1所示。
在Java中,数组一旦数组初始化完成,数组元素的内存空间分配即结束,此后程序只能改变数组元素的值,而无法改变数组的长度。但程序可以改变一个数组变量所引用的数组,从而造成数组长度可变的假象。同理,在复制数组时,直接使用赋值语句不能实现数组的复制,这样做只是使两个数组引用变量指向同一个数组对象。
//冒泡排序 //务必理解 趟数与轮数的控制
二维数组可以看成以数组为元素的数组,常用来表示表格或矩形。二维数组的声明、初始化与一维数组类似。
二维数组的声明,示例如下:
int[][] array;
int array[][];
二维数组动态初始化的实例如下:
array=new int[3][2]; // 动态初始化3*2的二维数组
array[0]={1, 2}; // 初始化二维数组的第一个元素
array[1]={3, 4}; // 初始化二维数组的第二个元素
array[2]={5, 6}; // 初始化二维数组的第三个元素
上述示例定义了一个3行2列的二维数组,即二维数组的长度为3,每个二维数组的元素是一个长度为2的一维数组。
二维数组静态初始化的示例如下:
array=new int[][]{
{1},
{2, 3},
{4}
};
对于二维数组的静态初始化也可用另一种形式,具体示例如下:
int[][] array={
{1},
{2, 3},
{4}
};
需要注意的是静态初始化由系统指定数组长度,不能进行手动指定。
二维数组中的每一行就是一个一维数组,因此,各行的长度就可以不同。这样的数组称为锯齿数组。创建锯齿数组时,可以只指定第一个下标,此时二维数组的每个元素为空,因此必须为每个元素创建一维数组。
Java中的方法定义在类中,一个类可以声明多个方法。方法的定义由方法名、参数、返回值类型以及方法体组成。
接下来说明如何定义方法,其语法格式如下:
修饰符 返回值类型 方法名([参数类型 参数名1,参数类型 参数名2,…]) {
方法体
return 返回值;
}
定义方法时需注意以下几点:
修饰符:方法的修饰符比较多,有对访问权限进行限定的,有静态
接下来演示方法声明,如图5.2所示。
在图3.2中,方法头中声明的变量称为形式参数,简称形参。当调用方法时,给参数传入的值称为实际参数,简称实参。形参列表是指形参的类型、顺序和数量。方法不需要任何参数,则形参列表为空。
方法可以有返回值,返回值必须为方法声明的返回值类型。如果方法没有返回值,则返回类型为void,return语句可以省略。
方法在调用时执行方法中的代码,因此要执行方法,必须调用方法。如果方法有返回值,通常将方法调用作为一个值来处理。如果方法没有返回值,方法调用必须是一条语句。具体示例如下:
int large = max(3, 4); // 将方法的返回值赋给变量
System.out.println(max(3,4)); // 直接打印方法的返回值
System.out.println("Hello World!");// 方法没有返回值,必须是语句
如果方法定义中包含形参,调用时必须提供实参。实参的类型必须与形参的类型兼容,实参顺序必须与形参的顺序一致。实参的值传递给方法的形参,称为值传递(pass by value),方法内部对形参的修改不影响实参值。当调用方法时,程序控制权转移至被调用的方法。当执行return语句或到达方法结尾时,程序控制权转移至调用者。
方法重载(overloading)是指方法名称相同,但形参列表不同的方法。调用重载的方法时,Java编译器会根据实参列表寻找最匹配的方法进行调用。
调用一个方法时,若出现两个或多个可能的匹配,编译器无法判断哪个是最精确的匹配,则会产生编译错误,这称为歧义调用(ambiguous invocation)。
方法只能根据参数列表(参数类型、参数顺序和参数个数)进行重载,而不能通过修饰符或返回值来重载。
方法的递归是指一个方法直接或间接调用自身的行为,递归必须要有结束条件,否则会无限地递归。递归用于解决使用简单循环难以实现的问题。
//递归方法
1) 递归方法求阶乘(Factorial)
1 n=0,1
n! =
n * (n-1)! n>=2
在方法调用时,参数按值传递,即用实参的值去初始化形参。对于基本数据类型,形参和实参是两个不同的存储单元,因此方法执行中形参的改变不影响实参的值;对于引用数据类型,形参和实参存储的是引用(内存地址),都指向同一内存单元,在方法执行中,对形参的操作实际上就是对实参数的操作,即对执行内存单元的操作,因此,方法执行中形参的改变会影响实参。
向方法传递数组时,方法的接收参数必须是符合其类型的数组;从方法返回数组时,返回值类型必须明确的声明其返回的数组类型。数组属于引用类型,所以在执行方法中对数组的任何操作,结果都将保存下来。
在程序开发初期人们使用结构化开发语言,但随着软件的规模越来越大,结构化语言的弊端也逐渐暴露出来,开发周期被延长,产品的质量也不尽人意,结构化语言已经不再适合当前的软件开发。这时人们开始将另一种开发思想引入程序中,即面向对象的开发思想。面向对象思想是人类最自然的一种思考方式,它将所有预处理的问题抽象为对象,同时了解这些对象具有哪些相应的属性以及展示这些对象的行为,以解决这些对象面临的一些实际问题,这样就在程序开发中引入了面向对象设计的概念,面向对象设计实际上就是对现实世界的对象进行建模操作。面向对象的特点主要可以概括为封装性、继承性和多态性,接下来针对这三种特性进行简单介绍。
封装是面向对象编程的核心思想。将对象的属性和行为封装起来,其载体就是类,类通常对客户隐藏其实现细节,这就是封装的思想。例如,用户使用计算机时,只需要使用手指敲击键盘就可以实现一些功能,无须知道计算机内部是如何工作的,即使知道计算机的工作原理,在使用计算机时也并不完全依赖于计算机工作原理这些细节。
类与类之间同样具有关系,如一个百货公司类与销售员类相联系,类之间的这种关系被称为关联。关联主要描述两个类之间的一般二元关系,例如,一个百货公司类与销售员类就是一个关联,学生类与教师类也是一个关联。两个类之间的关系有很多种,继承是其中的一种。
多态性允许以统一的风格编写程序,以处理种类繁多的已存在的类及相关类。该统一风格可以由父类来实现,根据父类统一风格的处理,可以实例化子类的对象。由于整个事件的处理都只依赖于父类的方法,所以日后只要维护和调整父类的方法即可。这样就降低了维护的难度,节省了时间。
类实质上就是封装对象属性和行为的载体,而对象则是类抽象出来的一个实例。
在类中,属性是通过成员变量体现的,而行为是成员函数(又称为方法)实现的,下面为大家演示Java中定义类的通用格式,其语法格式如下:
class 类名{
属性类型 成员变量名; // 成员变量(对象属性)
…
修饰符 返回值类型 方法名([参数列表]) { // 成员方法(对象行为)
// 方法体
return 返回值;
}
}
类是对象的抽象,为对象定义了属性和行为,但类本身既不带任何数据,也不存在于内存空间中。而对象是类的一个具体存在,既拥有独立的内存空间,也存在独特的属性和行为,属性还可以随着自身的行为而发生改变。接下来演示如何用类创建对象,创建对象之前,必须先声明对象,其语法格式如下:
类名 对象名;
类是自定义类型,也是一种引用类型,因此该对象名是一个引用变量,默认值为null,表示不指向任何堆内存空间。接下来需要对该变量进行初始化,Java使用new关键字来创建对象,也称实例化对象,其语法格式如下:
对象名 = new 类名();
对象实例化后,就可以访问对象的成员变量和成员方法,其语法格式如下:
对象名.成员变量;
对象名.成员方法();
封装是面向对象的三大特征之一,类的设计者将类设计成一个黑匣子,使用者只能通过类所提供的公共方法来实现对内部成员的操作和访问,而不能看见方法的实现细节,也不能直接访问对象内部成员。类的封装可以隐藏类的实现细节,迫使用户只能通过方法去访问数据,这样就可以增强程序的安全性。
如果想改变默认的初始化,让系统创建对象时就为该对象的成员属性显式地指定初始值,可以通过构造方法来实现。构造方法是类中一个特殊的成员方法,用于为类中属性初始化。
构造方法是在创建一个对象使用new关键字时被调用的。构造方法有三个特征:
如果类未定义任何构造方法,系统会自动提供一个默认构造方法,又称缺省构造方法。如果已存在带参数的构造方法,则系统将不会提供缺省构造方法。
每次定义类的构造方法时,应预先定义一个无参的构造方法,有参的构造方法可以根据需求再定义。
由于系统提供的默认构造方法通常不能满足需求,这时就需要多个构造方法,与普通方法一样,只要每个构造方法的参数列表不同,即可实现重载。这样在创建对象时,就可以通过调用不同的构造方法为不同的属性赋值。
类在定义成员方法时,局部变量和成员变量可以重名,但此时不能访问成员变量。为避免这种情形,Java提供了this关键字,表示当前对象,指向调用的对象本身。
this关键字可以明确调用类的成员变量,不会与局部变量名发生冲突。
this既然可以访问成员变量,那么也可以访问成员方法。
构造方法是在实例化时被自动调用的,因此不能直接像调用成员方法一样去调用构造方法,但可以使用this([实参列表])的方式调用其他的构造方法。
在使用this调用构造方法时,还需注意:在构造方法中,使用 this调用构造方法的语句必须位于首行,且只能出现一次。
另外,this调用构造方法时,一定要留一个构造方法作为出口,即至少存在一个构造方法不使用this调用其他构造方法。
在Java中,用new关键字创建对象或数组等引用类型时,都会在堆内存中为之分配一块内存,用于保存对象,当此块内存不再被任何引用变量引用时,这块内存就变成垃圾。Java引入了垃圾回收机制(Garbage Collection,GC)来处理垃圾,它是一种动态存储管理技术,由Java虚拟机自动回收垃圾对象所占的内存空间,不需要程序代码来显式释放。
当一个对象失去引用时,除了等待Java虚拟机自动回收之外,还可以调用System.gc()方法来通知Java虚拟机进行垃圾回收。该方法只是向Java虚拟机发出一个回收申请,至于Java虚拟机是否进行垃圾回收并不能确定。
当一个对象在内存中被释放时,Java虚拟机会自动调用该对象的finalize()方法,该方法用于在对象被垃圾回收机制销毁前执行一些资源回收工作。如果在程序终止前Java虚拟机始终没有执行垃圾回收操作,那么Java虚拟机将始终不会调用该对象的finalize()方法。在Java的祖先类中提供了finalize()方法,因此,所有的类都可以重写该方法,但需注意该方法没有任何参数和返回值,并且每个类中有且只有一个该方法。
static关键字表示静态的,用于修饰成员变量、成员方法以及代码块,如用static修饰main()方法。灵活正确地运用static关键字,可以使程序更符合现实世界逻辑。
使用static修饰的成员变量,称为静态变量或类变量,它被类的所有对象共享,属于整个类所有,因此可以通过类名直接来访问。而未使用static修饰的成员变量称为实例变量,它属于具体对象独有,只能通过引用变量访问。
使用static修饰的成员方法,称为静态方法,无须创建类的实例就可以调用静态方法,静态方法可以通过类名调用。
静态方法只能访问类的静态成员(静态变量、静态方法),不能访问类中的实例成员(实例变量和实例方法)。这是因为未被static修饰的成员都是属于对象的,所以需要先创建对象才能访问,而静态方法在被调用时可以不创建任何对象。
代码块是指用大括号“{}”括起来的一段代码,根据位置及声明关键字的不同,代码块可分为普通代码块、构造代码块、静态代码块和同步代码块,其中同步代码块在第7章进行讲解。
普通代码块就是在方法名后或方法体内用大括号“{}”括起来的一段代码。
构造代码块就是直接定义在类中的代码块,它没有任何前缀、后缀及关键字修饰。上节中提到,每个类中至少有一个构造方法,创建对象时,构造方法被自动调用,构造代码块也是在创建对象时被调用,但它在构造方法之前被调用,因此,构造代码块也可用来初始化成员变量。
静态代码块就是使用static关键字修饰的代码块,它是最早执行的代码块。
设计模式描述了软件设计过程中经常碰到的问题及解决方案,它是面向对象设计经验的总结和理论化抽象。通过设计模式,开发者就可以无数次地重用已有的解决方案,无需再重复相同的工作。
单例模式是设计模式中的一种,是指一个类在程序运行期间有且仅有一个实例,并且自行实例化向整个系统提供这个实例。例如Windows操作系统只提供一个任务管理器。单例类的一个重要特征是类的构造方法是私有的,从而避免了外部利用构造方法直接创建多个实例。
单例模式有3个特点,具体如下:
在Java中,类中除了可以定义成员变量与成员方法外,还可以定义类,该类称作内部类,内部类所在的类称作外部类。根据内部类的位置、修饰符和定义的方式可分为成员内部类、静态内部类、方法内部类以及匿名内部类4种。
内部类有3点共性:
私有成员),但外部类不能直接访问内部类成员。
public 和默认的访问权限。
成员内部类是指类作为外部类的一个成员,能直接访问外部类的所有成员,但在外部类中访问内部类,则需要在外部类中创建内部类的对象,使用内部类的对象来访问内部类中的成员。同时,若要在外部类外要访问内部类,则需要通过外部类对象去创建内部类对象,在外部类外创建一个内部类对象的语法格式如下:
外部类名.内部类名 引用变量名=new 外部类名().new 内部类名()
需要注意的是,成员内部类不能定义静态变量、静态方法和静态内部类。这是因为当外部类被加载时,内部类是非静态的,那么Java编译器就不会初始化内部类中的静态成员,这就与Java编译原则相违背。
如果不需要外部类对象与内部类对象之间有联系,那么可以将内部类声明为static,用static关键字修饰的内部类称为静态内部类。静态内部类可以有实例成员和静态成员,它可以直接访问外部类的静态成员,但如果想访问外部类的实例成员,就必须通过外部类的对象去访问。另外,如果在外部类外访问静态内部类成员,则不需要创建外部类对象,只需创建内部类对象即可。创建内部类对象的语法格式如下:
外部类名.内部类名 引用变量名=new 外部类名.内部类名()
若访问内部类的静态成员,则无须创建外部类和静态内部类对象,通过“外部类名.内部类名.静态成员”的形式访问。要访问内部类的实例成员,则需要创建静态内部类对象,通过“new 外部类名.内部类名()”形式直接创建内部类对象。
方法内部类是指在成员方法中定义的类,它与局部变量类似,作用域为定义它的代码块,因此它只能在定义该内部类的方法内实例化,不可以在此方法外对其实例化。
匿名内部类就是没有名称的内部类。创建匿名内部类时会立即创建一个该类的对象,该类定义立即消失,匿名内部类不能重复使用。
需要注意的是,匿名内部类是不能加访问修饰符的,而且被new的匿名类必须是先定义的。
继承是面向对象的另一大特征,它用于描述了类的所属关系,多个类通过继承形成一个关系体系。继承是在原有类的基础上扩展新的功能,实现了代码的复用。
在现实生活中,继承是指下一代人继承上一代人遗留的财产,即实现财产重用。在面向对象程序设计中,继承实现代码重用,即在已有类的基础上定义新的类,新的类能继承已有类的属性与行为,并扩展新的功能,而不需要把已有类的内容再写一遍。已有的类被称为父类或基类,新的类被称为子类或派生类。例如交通工具与公交车就属于继承关系,公交车拥有交通工具的一切属性,但同时又拥有自己独有的特性。在Java中,子类继承父类的语法格式如下:
class 子类名 extends 父类名 {
属性和方法
}
Java使用extends关键字指明两个类之间的继承关系。子类继承了父类中的属性和方法,也可以添加新的属性和方法。
Java语言只支持单继承,不允许多重继承,即一个子类只能继承一个父类,否则会引起编译错误,具体示例如下:
class A {}
class B {}
class C extends A, B {}
Java语言虽然不支持多重继承,但它支持多层继承,即一个类的父类可以继承另外的父类。因此,Java类可以有无限多个间接父类,具体示例如下:
class A {}
class B extends A {}
class C extends B {}
在继承关系中,子类从父类中继承了可访问的方法,但有时从父类继承下来的方法不能完全满足子类需要,如果要求父类与子类中的方法输出不同内容,这时就需要在子类的方法里修改父类的方法,即子类重新定义从父类中继承的成员方法,这个过程称为方法重写或覆盖。在进行方法重写时必须考虑权限,即被子类重写的方法不能拥有比父类方法更加严格的访问权限。
另外,需要注意方法重载与方法重写的区别:
当子类重写父类方法后,子类对象将无法访问父类被重写的方法。如果在子类中需要访问父类的被重写方法,可以通过super关键字来实现,其语法格式如下:
super.成员变量
super.成员方法([实参列表])
在继承中,实例化子类对象时,首先会调用父类的构造方法,再调用子类的构造方法,这与实际生活中先有父母再有孩子类似。子类继承父类时,并没有继承父类的构造方法,但子类构造方法可以调用父类的构造方法。在一个构造方法中调用另一个重载的构造方法使用this关键字,在子类构造方法中调用父类的构造方法用super关键字,其语法格式如下:
super([参数列表])
在Java中,为了考虑安全因素,要求某些类不允许被继承或不允许被子类修改,这时可以用final关键字修饰。它可用于修饰类、方法和变量,表示“最终”的意思,即用它修饰的类、方法和变量不可改变,具体特点如下:
使用final关键字修饰的类称为最终类,表示不能再被其他的类继承,如Java中的String类。
使用final关键字修饰的方法,称为最终方法,表示子类不能重写此方法。
使用final关键字修饰的变量,称为常量,只能被赋值一次。如果再次对该变量进行赋值,则程序在编译时会报错。
Java中可以定义不含方法体的方法,方法的方法体由该类的子类根据实际需求去实现,这样的方法称为抽象方法(abstract method),包含抽象方法的类必须是抽象类(abstract class)。
Java中提供了abstract关键字,表示抽象的意思。用abstract修饰的方法,称为抽象方法,是一个不完整的方法,只有方法的声明,没有方法体。用abstract修饰的类,称为抽象类,抽象类可以不包含任何抽象方法,具体示例如下:
// 用abstract修饰抽象类
abstract class Parent {
// abstract修饰抽象方法,只有声明,没有实现
public abstract void say();
}
使用抽象类时需要注意,抽象类不能被实例化,即不能用new关键字创建对象,因为抽象类中可包含抽象方法,抽象方法只有声明,没有方法体,不能被调用。因此,必须通过子类继承抽象类去实现抽象方法。
需要注意的是,具体子类必须实现抽象父类中所有抽象方法,否则子类必须声明为抽象类。
另外,抽象方法不能用static来修饰,因为static修饰的方法可以通过类名调用,调用时将调用一个没有方法体的方法,肯定会出错;抽象方法也不能用final关键字修饰,因为被final关键字修饰的方法不能被重写,而抽象方法的实现需要在子类中实现;抽象方法也不能用private关键字修饰,因为子类不能访问带private关键字的抽象方法。
抽象类中可以定义构造方法,因为抽象类仍然使用的是类继承关系,而且抽象类中也可以定义成员变量。因此,子类在实例化时必须先对抽象类进行实例化。
接口是全局常量和公共抽象方法的集合,接口可被看作一种特殊的类,也属于引用类型。每个接口都被编译成独立的字节码文件。Java提供interface关键字,用于声明接口,其语法格式如下:
interface 接口名{
全局常量声明
抽象方法声明
}
接下来演示interface关键字的作用,具体示例如下:
// 用interface声明接口
interface Parent {
String name;// 等价public static final String name;
void say(); // 等价public abstract void say();
}
接口中定义的变量和方法都包含默认的修饰符,其中定义的变量默认声明为“public static final”,即全局常量。另外定义的方法默认声明为“public abstract”,即抽象方法。
与抽象类相似,接口中也包含抽象方法。因此,不能直接实例化接口,即不能使用new创建接口的实例。Java提供implements关键字,用于实现多个接口,其语法格式如下:
class 类名 implements 接口列表{
属性和方法
}
在Java中使用extends关键字来实现接口的继承,它与类的继承类似,当一个接口继承父接口时,该接口会获得父接口中定义的所有抽象方法和常量;但又与类的继承不同,接口支持多重继承,即一个接口可以继承多个父接口。其语法格式如下:
interface 接口名 extends 接口列表 {
全局常量声明
抽象方法声明
}
需要特别指出的是,任何实现继承接口的类,必须实现该接口继承的其他接口,除非类被声明为abstract。
抽象类与接口是Java语言中对于抽象类定义进行支持的两种机制,两者非常相似,初学者经常混淆这两个概念,两者的相同点可以归纳为三点:
下表列出了两者之间的区别,如表6.1所示。
区别点 |
接口 |
抽象类 |
含义 |
接口通常用于描述一个类的外围能力,而不是核心特征。类与接口之间的是-able或者can do的关系。 |
抽象类定义了它的后代的核心特征。派生类与抽象类之间是is-a的关系。 |
方法 |
接口只提供方法声明。 |
抽象类可以提供完整方法、缺省构造方法以及用于覆盖的方法声明。 |
变量 |
只包含public static final常量,常量必须在声明时初始化。 |
可以包含实例变量和静态变量。 |
多重继承 |
一个类可以继承多个接口。 |
一个类只能继承一个抽象类。 |
实现类 |
类可以实现多个接口。 |
类只从抽象类派生,必须重写。 |
适用性 |
所有的实现只是共享方法签名。 |
所有实现大同小异,并且共享状态和行为。 |
简洁性 |
接口中的常量都被默认为public static final,可以省略。接口中的方法被默认为public abstract。 |
可以在抽象类中放置共享代码。必须用abstract显式声明方法为抽象方法。 |
添加功能 |
如果为接口添加一个新的方法,则必须查找所有实现该接口的类,并为它们逐一提供该方法的实现。 |
如果为抽象类提供一个方法,可以选择提供一个缺省的实现,那么所有已存在的代码不需要修改就可以继续工作。 |
总体来说,抽象类和接口都用于为对象定义共同的行为,两者在很大程度上是可以互相替换的,但由于抽象类只允许单继承,所以当两者都可以使用时,优先考虑接口,只有当需要定义子类的行为,并为子类提供共性功能时才考虑选用抽象类。
多态是面向对象的另一大特征,封装和继承是为实现多态做准备的。简单来说,多态是具有表现多种形态的能力的特征,它可以提高程序的抽象程度和简洁性,最大程度降低了类和程序模块间的耦合性。
多态是指同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在Java程序中,多态是指把类中具有相似功能的不同方法使用同一个方法名实现,从而可以使用相同的方式来调用这些具有不同功能的同名方法。
Java中的引用变量有两种类型,即声明类型和实际类型。变量声明时被指定的类型称为声明类型,而被变量引用的对象类型称为实际类型。方法可以在沿着继承链的多个类中实现,当调用实例方法时,由Java虚拟机动态地决定所调用的方法,称为动态绑定。
动态绑定机制原理是:当调用实例方法时,Java虚拟机从该变量的实际类型开始,沿着继承链向上查找该方法的实现,直到找到为止,并调用首次找到的实现。
对象的类型转换是指可以将一个对象的类型转换成继承结构中的另一种类型。类型转换分为两种,具体如下:
需要特别注意的是,向下转型时,被转换变量的实际类型,必须是转换类或其子类。
Java提供了instanceof关键字,用于判断一个对象是否是一个类(或接口)的实例,表达式返回boolean值,其语法格式如下:
变量名 instanceof 类名
Java中提供了一个Object类,是所有类的父类,如果一个类没有显式地指定继承类,则该类的父类默认为Object。例如,下面两个类的定义是一样的:
class ClassName {}
class ClassName extends Object {}
在Object类中提供了很多方法,接下来分别对其中的方法进行解释,如表6.2所示。
方法声明 |
功能描述 |
public String toString() |
返回描述该对象的字符串 |
public Boolean equals(Object o) |
比较两个对象是否相等 |
public int hashCode() |
返回对象的哈希值 |
暂时只对toString()和equals方法进行讲解,而hashCode()方法在Java集合中再详细讲解。
调用一个对象的toString()方法会默认返回一个描述该对象的字符串,它由该对象所属类名、@和对象十六进制形式的内存地址组成。
通常,重写toString()方法返回对象具体的信息。
equals()方法是用于测试两个对象是否相等。
如果要检测两个不同对象的内容是否相同,就必须重写equals()方法。例如String类中的equals()方法继承自Object类并重写,使之能够检验两个字符串的内容是否相等。
工厂模式主要用来实例化有共同接口的类,它可以动态决定应该实例化哪一个类,不必事先知道每次要实例化哪一个类。工厂模式主要有三种形态:简单工厂模式、工厂方法模式和抽象工厂模式。接下来分别对这三种形态进行讲解。
简单工厂模式又称静态工厂方法,它的核心是类中包含一个静态方法,该方法用于根据参数来决定返回实现同一接口不同类的实例。
工厂类SimpleFactory有很多局限。首先,维护和新增产品时,都必须修改SimpleFactory源代码。其次,如果产品之间存在复杂的层次关系,则工厂类必须拥有复杂的逻辑判断。最后,整个系统都依赖SimpleFactory类,一旦SimpleFactory类出现问题,整个系统就将瘫痪不能运行。
工厂方法模式为工厂类定义了接口,用多态来削弱了工厂类的职责。
工厂方法模式也有局限之处,当面对有复杂的树形结构的产品时,就必须为每个产品创建一个对应的工厂类,当达到一定数量级就会出现类爆炸。
抽象工厂用于创建一系列互相关联或互相依赖的对象。
代理模式是指给某一个对象提供一个代理,并由代理对象控制对原有对象的引用。如生活中,求职者找工作(真实操作),可以让猎头帮忙去找(代理操作),猎头把最终结果反馈给求职者。无论是真实操作还是代理操作,目的都是一样的,求职者只关心最终结果,而不关心过程。
当声明的类很多时,类名就有可能冲突,这就需要一种机制来管理类名,因此,Java中引入了包机制,本节将详细介绍包的用法。
包(package)是Java提供的一种区别类的名字空间的机制,是类的组织方式,是一组相关类和接口的集合,它提供了访问权限和命名的管理机制。
使用package语句声明包,其语法格式如下:
package 包名
使用时需要注意以下四点:
包与文件目录类似,可以分成多级,多级之间用“.”符号进行分隔,具体示例如下:
package com.l000phone.www;
如果在程序中已声明了包,就必须将编译生成的字节码文件保存到与包名同名的子目录中,可以使用带包编译命令,具体示例如下:
javac -d . Source.java
其中,“-d”表示生成以package定义为准的目录,“.”表示在当前所在的文件夹中生成。编译器会自动在当前目录下建立与包名同名的子目录,并将生成的.class文件自动保存到与包名同名的子目录下。
接下来分步骤讲解包机制管理Java源文件:
(1)在源文件首行声明包。
(2)使用“javac -d . TestPackage.java”编译源文件。
(3)执行命令完成后,在当前目录下会生成包名对应的目录。
(4)使用“java com.l000phone.www.TestPackage”命令运行程序,运行带包名的字节码文件时,必须输入完整的“包.类名称”。
在实际开发中,项目都是分模块开发,对应模块包中的类,完成相应模块的功能。但有时模块之间的类要相互调用。例如,通常开发中都是将业务逻辑层的接口和实现放在不同包中。
为了简化编程,Java提供了import关键字,用于导入指定包层次下某个类或全部类,import语句应放在package语句之后,类定义之前,其语法格式如下:
import 包名.类名 // 导入单类
import 包名.* // 导入包层次下的全部类
在JDK5.0后提供了静态导入功能,用于导入指定类的某个静态成员变量、方法或全部的静态成员变量、方法,具体示例如下:
import static 包名.类名.成员 // 导入类指定静态成员
import static 包名.类名.* // 导入该类全部静态成员
import语句和import static语句之间没有任何顺序要求。使用import导入包后,可以在代码中直接访问包中的类,即可以省略包名;而使用import static导入类后,可以在代码中直接访问类中静态成员,即可以省略类名。
Java的核心类都放在java包及其子包下,Java扩展的类都放在javax包及其子包下,接下来了解一下常用的开发包,如表6.3所示。
包名 |
功能描述 |
java.lang |
核心包,如String、Math、System类等,无需使用import手动导入,系统自动导入 |
java.util |
工具包,包含工具类、集合类等,如Arrays、List和Set等 |
java.net |
包含网络编程的类和接口 |
java.io |
包含输入、输出编程相关的类和接口 |
java.text |
包含格式化相关的类 |
java.sql |
数据库操作包,提供了各种数据库操作的类和接口 |
java.awt |
包含抽象窗口工具集(abstract window toolkits)相关类和接口,主要用于构建图形用户界面(GUI) |
java.swing |
包含图形用户界面相关类和接口 |
在实际开发中,通常会将一些类提供给别人使用,直接提供字节码文件会比较麻烦,所以一般会将这些类文件打包成jar文件,以供别人使用。jar文件的全称是Java Archive File,意思就是Java归档文件,也称为jar包。将一个jar包添加到classpath环境变量中,Java虚拟机会自动解压jar包,根据包名所对应的目录结构去查找所需的类。
通常使用jar命令来打包,可以把一个或多个路径压缩成一个jar文件。jar命令是在JDK安装目录下的bin目录中,直接在命令行中输入jar命令,即可查看jar命令的提示信息。
jar命令主要参数如下:
接下来分步骤学习如何用jar命令进行文件打包及运用jar包:
(1)打开命令行窗口,切换到将要被打包目录的上级目录,输入命令:
jar -cvf common.jar com
上面命令是将com目录下的全部内容打包成common.jar文件,如果硬盘上有同名jar将被覆盖。
(2)输入“set classpath=.;D:\com\1000phone\chapter05\05\common.jar”命令,将jar包添加到环境变量中。
(3)删除生成的包目录及User.class文件。编写测试类,测试jar包是否可用。
(4)查看jar包命令如下:
jar -tvf jar文件名
如查看common.jar文件,则输入命令“jar -tvf common.jar”。
(5)解压jar包命令如下:
jar -xvf jar文件名
如解压common.jar文件,则输入命令“jar -xvf common.jar”,参数x代表从归档中提取指定的文件。
有了包的概念之后,接下来将系统介绍Java中访问权限的概念。它可以对类、属性以及方法进行声明和控制,以便隐藏类的一些实现细节,防止对封装数据未经许可的访问和不合理的操作。
Java中的4种访问权限级别由小到大依次如下:
接下来总结4种访问控制权限,如表5.4所示。
访问范围 |
private |
default |
protected |
public |
同一类中成员 |
√ |
√ |
√ |
√ |
同一包中其他类 |
|
√ |
√ |
√ |
不同包中子类 |
|
|
√ |
√ |
不同包的非子类 |
|
|
|
√ |
二
pta实践总结
题目1
编写一个程序,输入n个元素的一维数组,然后按逆序重新存放。
代码:package javaoo;
import java.util.Scanner;
public class test{
public static void main(String[] args){
int[] arr = {1,2,3,4,5,6,7,8,9,10};
System.out.println("原顺序:");
for(int i =0;i<arr.length;i++) {
System.out.print(arr[i]+" ");
}
for(int i =0;i<arr.length/2;i++) {
int temp = arr[arr.length-1-i];
arr[arr.length-1-i]=arr[i];
arr[i]=temp;
}
System.out.println();
System.out.println("逆顺序:");
for(int i =0;i<arr.length;i++) {
System.out.print(arr[i]+" ");
}
}
}
题目2
从键盘输入圆半径和圆柱体的高,分别计算圆周长,圆面积,圆球表面积,圆球体积和圆柱体的体积,并输出结果。
代码:package javaoo;
import java.util.Scanner;
public class test{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
double r,h;
r = sc.nextDouble();
h = sc.nextDouble();
double c = 2*3.14*r;
double s = 3.14*r*r;
double m = 4*3.14*r*r;
double v = 4*r*r*r/3;
double l = s*h;
System.out.println(c);
System.out.println(s);
System.out.println(m);
System.out.println(v);
System.out.println(l);
sc.close();
}
}
题目3
编程输入整数n,输入n个学生的java成绩,计算所有学生的成绩平均分和最高分、最低分,并输出。
代码:package javaoo;
import java.util.Scanner;
public class test{
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
double sz[]=new double[n];
for(int i=0;i<n;i++){
sz[i]=sc.nextDouble();
}
double sum=0;
for(int i=0;i<n;i++){
sum += sz[i];
}
System.out.println("平均分是:"+sum/n);
double max = sz[0];
for (int i = 0; i < sz.length; i++) {
if (sz[i] > max) max = sz[i];
}
System.out.println("最高分是:"+max);
double min = sz[0];
for (int i = 0; i < sz.length; i++) {
if (sz[i] <min) min = sz[i];
}
System.out.println("最低分是:"+min);
}
}
三
改进学习方法,措施落实情况
3.1
除课堂外,遇到不会的问题时会到哪个平台搜索
CSDN 夸克
3.2感觉个人的哪些方法还存在不足?哪些方面需要改进?
不足:做题时思路不清晰,头脑转的不够快,练的题少
改进:在做题时先分析好每一个步骤,多去刷题
四
你知道csdn么,结合使用情况表述他的优点与不足
4.1 csdn给你的直观感受是什么?
使用便捷
4.2 csdn你感觉最不舒服的地方是什么?
在查找代码时,查到的数量太多
五
学习编程的最大阻力?怎样克服?
阻力:基础不扎实,做题的数量不够多,在做题时思路不够清晰,只能走一步算一步
克服:平常时多去刷点有代表性的题目,同时也要去稳固一下自己的基础知识
六.
其他想法
平常时,可以去B站看一下程序课程的学习