java必须精通第一课

基础知识

编码

在计算机的世界里,所有的文字、数值都只是一连串的0和1,这些0和1对于设计者来说实在难以理解,于是就产生了各种方式的编码,它们通过指定的一个数值来表示某个字符,如常用的ASCII编码。
虽然各类的编码系统合起来有数百中之多,却没有一种包含足够的字符、标点符号及常用专业技术符号。这些编码系统之间可能还会有相互冲突的情况发生。也就是说不同的编码系统可能会使用相同的数值来表示不同的字符,在数据跨平台就会发生错误。
Unicode就是为了避免上述情况发生,它为每个字符定制了一个唯一的数值,因此在任何语言、平台、程序中都可以放心的使用。java所使用的就是Unicode字符码系统。

数据类型

java是一种强类型语言,这就意味着为每一个变量生命一种类型,在java中一共有八种基本数据类型。其中四种整型,两种浮点型,一种用于表示Unicode编码的字符类型和一种用于表示真假的Boolean类型
八种具体数据类型
在java中整型的类型与运行java代码的机器无关,这就解决了软件从一个平台移植带另一个平台,或者在同一平台的不同系统之间移植给程序员带来的诸多问题。因为java程序必须保证在所有机器上能够得到相同的运行结果,所以每一种数据类型取值固定。(C和C++就不同,它们的基本数据类型可能因为机器的不同而采用的字节长度也不一样)

长整型数值有一个后缀L(如4000000000L)。十六进制有一个前缀0x,八进制有个一0,(比如010对应八进制中的8)。很显然八进制的表示容易混淆,所以建议最好不要使用八进制常熟。

浮点类型

double这种数据类型的精度是float精度的两倍。绝大多数应用程序都用double数据类型。
float数值带有后缀F,没有的默认表示double类型。
double后缀可以没有后缀,也可以带有一个D的后缀

表示溢出和出错的三种特殊的浮点数值:
正无穷,负无穷,NaN(不是一个数字)
例如,一个正整数除以0结果为正无穷大。 计算0/0或者负数的平方根,结果为NaN。
浮点数值不适用于禁止出现的舍入误差的金融计算中如:System.out.println(2.0-1.1)将打印出0.8999999999999999,而不是人们想象的0.9。主要原因是浮点数值,采用二进制表示,而在二进制系统中无法精确的表示分数1/10,这就好像十进制无法精确表示1/3一样,如果需要在计算中不含有任何误差,就应该使用BigDecimal类。

char类型用UTF-16编码描述一个代码单元。Unicode是为了统一各个国家的通用编码,就是国际编码。
强烈建议不要在程序中使用char类型,除非确实需要对UTF-16代码单元进行操作,最好将需要处理的字符串用抽象数据表示

包装类型

Integer、Byte、Float、Double、Short、Long都属于Number的子类
Character属于Object的直接子类
Boolean属于Object的直接子类
Number是一个抽象类,主要是将数字中的包装类中的内容变成基本数据类型。
Number类定义

Integer类字符串转int
public static int parseInt(String s)throws NumberFormatException
Float类  字符串转float
public static float parseFloat(String s)throws NumberFormatException
使用这两种操作时候。一定要注意字符串必须由数字组成。

变量

可以在一行声明多个变量 int a,b;不过不提倡这种方式,逐一生成变量会提高程序的可读性
变量名大小写敏感
声明一个变量以后必须用赋值语句对变量进行初始化可以将变量的声明和初始化放在一起,也可以分开。没有被初始化的变量不能使用。

常量

final 关键字声明常量,final关键字表示这个变量只能被赋值一次,一旦赋值之后就不能更改了,习惯上,常量名字使用大写。
在java中,经常希望某个常量可以在一个类中多个方法使用,通常将这些常量生命为类常量,可以使用关键字static final设置一个类常量(类常量声明为全局变量)如果一个类常量被声明为public,那么其他类的方法也可以使用这个常量

运算符

15/2=7 15%2=1 15.0/2=7.5
整数被0除将会产生一个异常,而浮点型数被0除将会得到一个无穷大或者NaN的结果
三元运算符,如x<y?x:y 返回x和y中较小的那个值
java中求一个数的平方根用sqrt,如:double x=Math.sqrt(y);
java中的幂运算用pow,如x的a次幂—double b=Math.pow(x,a);
在Math类中,为了达到最快的性能,所有的方法都使用计算机浮点单元中的例程,如果得到一个完全可以预测的结果比运行速度重要的话,那么就应该使用StrictMath类

强制类型转换,double x=9.78; int y=(int)x; 这样y的值就变成9.强制类型转换通过截断小数部分将浮点值转换为整型。如果想对浮点型进行舍入计算,以便得到最接近的整数,需要使用Math.round方法。如:
int y=(int)Math.round(x)现在y的值变为10
需要注意的是上面使用round进行舍入运算时依然进行了int的强制转换,是因为round方法返回的是long类型,你用int去接返回值,所以依然需要进行强制转换。

运算符&& || 具有短路功能 & | 没有短路功能。
位运算符
位运算
&与运算 相同为1.不同为0
|或运算 含有就为1
^异或运算 相同为0,不同为1

运算符优先级
优先级

字符串

java字符串就是Unicode字符序列
子串:String a="Hello"; String b=a.substring(0,3); b的结果是Hel 截取长度为下标0到2
字符串拼接
将一个字符串和一个非字符串拼接,非字符串会自动转化为字符串。
不可变字符串
如同上面的字符串a一样,永远都是H e l l o这个单元序列,如果真要修改字符串。也可以用一种方式,就是首先提取需要的字符,之后拼接上。
如:把Hello修改成Help:String a=a.substring(0,3)+“p”; 这种方式也是修改了指向,而没有真正的修改了Hello这个字符串,内存空间还是存在这个字符串的。

spilt和replaceAll两个方法是String对正则表达式的支持
字符串转换字符数组
char[] chars=s.toCharArray();
字符数组转换字符串 
String ss=new String(chars);
把一个字符串变成byte数组
byte[] bytes=s.getBytes();
String s="hello";
s=s+"world";
这种情况,在内存中结构是这样的
栈内存           堆内存
s                      "hello"
					   "world"
					   "helloworld"
s一开始指针指向“hello”这个字符串,只有改变了指向,指向了“helloworld”这个字符串,
但是真正的hello字符串并没有发生改变,改变的只是引用。

由于不能修改java字符串中的字符,所以在java文档中将String类的对象称为不可变字符串。就如同数字三永远是数字三一样。但是可以修改字符串变量的指向,可以指向一个新的字符串。
java编译器的神奇地方就是让字符串共享,就好像创建一个公共的空间,让变量指向存储池中相应字符串的位置。共享的效率远远高于提取和拼接字符串的效率
equals
equals是检测两个字符串内容是否相等,检查指向地址是否相等用==
相等返回true,不相等返回false。
使用 equalseIgnoreCase方法忽略字符串大小写。
equals比较时,可以使用字符串比较也可以使用变量比较,也可以字符串和变量混用。如:a.equalse(“Hello”)

一定不能使用==检测两个字符串是否相等,==这个运算符只能确定两个字符串是否在同一位置上,	当然如果字符串放在同一位置上,它们必然相等,但是完全有可能将内容相同的多个字符串的拷贝放在不同的位置上。

字符串API(常用)

cahr charAt(index)
返回给定位置的代码单元,除非对底层感兴趣,否则不用调用这个代码。
int compareTo(String other)
按照字典顺序,如果字符串位于other之前,返回一个负数,如果字符串位于other之后,返回一个正数,如果两个字符串相等返回0
boolean endsWith(String suffix)
如果字符串以suffix结尾,返回true
boolean equals(Object other)
如果字符串与other相等返回true
boolean equalsIgnoreCase(Object other)
如果字符串与other相等返回ture(忽略字符串大小写来进行比较)
int indexOf(String str)
int indexOf(String str,int fromIndex)
int indexOf(int cp)
int indexOf(int cp,int fromIndex)
返回字符串str或者代码点匹配的第一个子串开始的位置。这个位置从索引0或者fromIndex开始计算,如果在原始串中不存在str,返回-1。
int lastIndexOf(String str)
int lastIndexOf(String str,int fromIndex)
int lastIndexOf(int cp)
int lastIndexOf(int cp,int fromIndex)
返回与字符串str或代码点cp匹配的最后一个字符串开始的位置,这个位置从原始串尾端或从fromIndex开始计算。
int length()
返回字符串长度
String replace(CharSequence oldString,CharSquence newString)
返回一个新字符串,这个字符串用newString代替原始字符串中所有的oldString。可以用String或者StringBuilder对象作为CharSquence参数。
String toLowerCase()
返回一个新字符串,这个字符串将原始的字符串中的所有大写字母改成小写字母。
String toUpperCase()
返回一个新字符串,这个字符串将原始的字符串所有的小写字母改成大写字母。
String trim()
返回一个新的字符串,这个字符串删除原始字符串头部和尾部的空格。

构建字符串
有时候需要构建字符串,采用字符串连接的方式达到此目的效率较低。每次连接字符串,都会构建一个新的String对象,既耗时又浪费空间。使用StringBuilder类就可以避免这个问题的发生。
如果需要用许多的小段的字符串构建一个字符串,那么应该按照下列的步骤进行。首先构造一个空的字符串构造器。
StringBuilder builder=new StringBuilder();
当每次添加一部分内容的时候就用append方法:
builder.append(“cui”);
builder.append(“cg”);

在需要构建字符串时就调用toString方法。
String result=builder.toString();
StringBuffer是StringBuilder的前身,StringBuffer支持多线程,但是效率低。
StringBuilder不支持多线程,但是效率高。这也是为什么后来引入StringBuilder的原因(提高效率)

**StringBuilder API**
int length()
返回构建器或者缓冲器中的代码单元数量。
StringBuilder append(Stringc)
追加一个代码单元并返回this
StringBuilder append(char c)
追加一个代码单元并返回this
void setCharAt(int i,char c)
将第i个代码单元设置为c
StringBuilder insert(int offset,String str)
在offset处插一个字符串并返回this。
StringBuilder insert(int offset	,Char C)
在offset处插一个代码单元并返回this。
StringBuilder delete(int startIndex,int endIndex)
删除偏移量从startIndex到endIndex-1的代码单元并返回this
String toString()
返回一个与构建器或缓冲器内容相同的字符串。

读取输入

标准的输出流(即控制台窗口)是一件非常容易的事,只要调用System.out.printlin()即可。然而标准的输入流System.in,就需要用Scanner对象了。

构建一个标准的输入流:
首先构造一个Scanner对象,并与标准的输入流System.in关联
Scanner san=new Scanner(System.in);
String name=san.nextLine();//nextLine方法将输入一行(会包括空格)
String name1=san.next();//读取一个单词,以空白符作为分隔
int age=san.nextInt();//取一个整数
double num=san.nextDouble();//读取一个浮点数

Scanner输入的都是可见的,所以不适用于从控制台中读取密码。要想隐式读取一个密码用Console类实现这个目标。

boolean hasNext() //检测输入中是否含有其他单词。
boolean hasNextInt()
boolean hasNextDouble()
//检测是否还有整数或者浮点数的下一个字符序列

文件的输入与输出

		//构建一个Scanner对象对文件进行读取
		Scanner file=new Scanner(new File("D:\\cui\\temp\\my.txt"));
		//构建一个PrintWriter对象对文件进行写入
		PrintWriter pw=new PrintWriter(new File("D:\\cui\\temp\\my.txt"));
		//print() 方法等同于 write() 方法
		//println() 方法是在 print() 的基础上多调用了一个 newLine() 方法(私有方法)
		//而 newLine() 方法会调用 flush(),所以关键在于 flush()
		pw.print("I like cat!");
		pw.print("\r\n");
		pw.print("I hate you!");
		pw.close();
		StringBuilder sb=new StringBuilder();
		while(file.hasNext()) {
			sb.append(file.nextLine());
		}

Scanner(File f)   //构造一个从给定文件读取数据的Scanner
Scanner(String s)   //构造一个从给定字符串读取数据的Scaner
PrintWriter(File f)   //构造一个将数据写入给定文件的PrintWriter
PrintWrite(Stirng fileName)   //构造一个将数据写入文件的PrintWriter,文件名由参数指定
File(String filename)   //用给定的文件名,构造一个描述文件File对象。注意这个文件当前不必存在。

大数值

如果基本的整数和浮点数精度不能满足需求,那么可以使用java.math包中的两个很有用的类。
BigInteger和BigDecimal。这两个类可以处理包含任意长度数字序列的数值。BigInteger类实现了任意精度的整数运算。BigDecimal实现了任意精度的浮点型运算。使用静态的valueOf可以将普通数值转换为大数值。

BigInteger number=BigInteger.valueOf(100);
遗憾的是,不能使用人们熟悉的算术运算符(+,-,* ,/等)处理大数值。
应该使用大数值类中的add(加)、subtract(减),multiply(乘),divide(除),mod(取余)。

数组

使用java中的数组,必须经过声明数组和开辟内存给数组两个步骤。声明数组时,会在栈内存开辟空间,只开辟栈空间的数组是无法使用的,必须有指向堆内存空间才能够使用,可以使用new关键字开辟堆内存空间,同时指定开辟空间的大小。
数组的两种声明方式
int[] a;或者int a[];    数组下标从0开始。
数组元素个数array.length;
数组必须初始化,否则不能使用。
int a[]=new int[10];  //第一种以指定大小的方式初始化数组。
int b[]= {1,2,3,4,5};   //第二种直接指定元素的方式指定大小。
一旦创建数组,就不能改变它的大小。(可以随意改变每一个数组中的元素)
如果需要经常改变数组的大小,可以创建动态数组(数组列表 array list)。
在java中允许将一个数组赋值拷贝给另一个数组,这时两个变量引用同一个数组。发生的元素改变都是一样的,因为引用一样。
int b[]= {1,2,3,4,5};
int a[]=b;
如果希望希望讲一个数组中的所有值拷贝到一个新的数组中去,就用到Arrays的copyOf方法
int b[]= {1,2,3,4,5};
int a[]=Arrays.copyOf(b, b.length);
如果 新的数组长度不够,只截取最前面的元素。

-----------数组排序----------
使用Arrays类中sort方法对数组进行排序。
int a[]= {1,8,3,6,5};
Arrays.sort(a);默认递增排序	

static int binarySearch(type[] a,type v)
static int binarySearch(type[] a,int start,int end,type v)
采用二分搜索算法查找v,如果查找成功则返回对应的下标值,否则,返回一个负数r

static void fill(type[] a,type v)
将数组的所有元素设置为v

static boolean equalse(type[] a,type[] b)
如果两个数组大小相等,下标对应的元素也相等,返回true,否则返回false

----------多维数组----------
二维数组定义
int a[][]= {{1,2},{3,4},{5,6}};   
//也有两种方式直接赋值初始化和指定大小初始化,基本要求同一维数组一样。

访问元素 a[i][j]这种方式。
要访问二维数组所有元素,需要两层for循环才可以。
快速打印二维数组列表可以使用如下方式:
Arrays.deepToString(a)这种方式。

for循环和foreach循环

for循环会使用下标值,foreach不需要考虑下标。
for循环可以不用遍历数组中的所有数值,foreach会处理集合中的所有元素。
打印数组中的所有值,有个最简单的方法就是Arrays类中的toString方法。如:Arrays.toString(b)
数组长度允许为0

引用传递

引用传递示例一:
public class Test {
	int temp=0;
public static void main(String[] args) {
		Test t1=new Test();
		t1.temp=20;
		System.out.println(t1.temp);
		fun(t1);
		System.out.println(t1.temp);
	}
	public static void fun(Test t2) {
		t2.temp=100;
	}
}
结果是: 
20
100
原理如下图

引用传递

引用传递示例二
public class Test {
public static void main(String[] args) {
			String str1=“hello”;
			System.out.println(str1);
			fun(str1);
		System.out.println(str1);
	}
	public static void fun(String str2) {
		str2="MLDN";
	}
}
结果是: 
hello
hello
从程序的运行结果来看,虽然此时传递的是一个String对象,但是结果并没有像之前一样发生改变,因为字符串的内容一旦被声明是不可改变的。改变的只是其内存地址的指向。

原理如下图:
字符串值引用

如果把String变成对象的属性修改的时候,比如第一个例子,把temp变成String类型,
那么结果就会和第一个例子一样。
this关键字

this可以使用this强调本类的方法

this表示当前对象

this表示类中的属性
class Test{
	private String name;
	private int age;
	public Test(String name,int age) {
		name=name;
		age=age;
	}
	public String getInfo() {
		return "姓名"+name+"年龄"+age;
	}
	public static void main(String[] args) {
		Test p=new Test("王五",33);
		System.out.println(p.getInfo());
	}
	
}
结果:
姓名null年龄0
构造函数里面的两个name就是入参的name属性。age也如此。内部机制就近取值。

用this区分后:
this.name=name;
this.age=age;

结果正常:
姓名王五年龄33
可以使用this调用本类的构造方法
class Test{
	private String name;
	private int age;
	public Test() {
		System.out.println("我是无参的构造函数===");
	}
	public Test(String name,int age) {
		this();
		this.name=name;
		this.age=age;
		//this();   //错误的调用只能放在构造方法的首行
	}
	public String getInfo() {
		//this();   //只能放在构造方法中并且是首行
		return "姓名"+name+"年龄"+age;
	}
	public static void main(String[] args) {
		Test p=new Test("王五",33);
		System.out.println(p.getInfo());
	}
	
}
static

如果使一个类分别开辟栈内存和堆内存,在堆内存中要保存对象中的属性,每个对象都有自己的属性,如果现在有些属性希望被所有属性共享,则就必须将其声明为static属性。如果一个类中的方法想由类调用,则可以声明为static属性。
如果有一个属性需要修改,那么就可以动态修改数值。而不用非得改动程序

没有static
String address=“北京”
City c1 = new City(“赵”,"11")
City c2 = new City(“钱”,"12")
City c3 = new City(“孙”,"13")
输出的是
 “赵”,"11" “北京”
 “钱”,"12" “北京”
 “孙”,"13" “北京”
 现在address地址改变了  怎么办,没有static的话 就需要自己去修改程序或者产生的对象记录。
 有static的话
static String address=“北京”
直接c1.address=“”上海”
这样就修改了所有记录对象。

一个类中的公共属性现在由一个对象修改,这样操作合适吗?很明显不合适,类的公共属性应该由
类进行修改是最适的,(就是City本身,而不是他生成的c1、c2等引用对象)
因为一个用户不知道一个类产生多少对象。
所以上面的代码在访问static属性时最后可以类名直接调用。如City.address="上海";
java中常用的内存区域:
1.栈内存空间:保存所有的对象名称(更准确的说是保存了引用堆内存空间的地址)
2.堆内存空间:保存每个对象的具体属性内容。
3.全局数据区:保存static类型的属性
4.全局代码区:保存所有的方法定义

static修饰方法
static修饰的方法被称为“类方法”,用类名.方法名就可以调用
不能使用static方法或者属性,调用非static的方法和属性。

static应用
可以使用static判断一类实例化了多少对象
class Test{
	static int count;
	public Test() {
		count++;
		System.out.println("生成了对象数量"+count);
	}
	public String getInfo() {
		//this();   //只能放在构造方法中并且是首行
		return "姓名"+"年龄";
	}
	public static void main(String[] args) {
		Test p=new Test();
		Test p2=new Test();
		Test p3=new Test();
		Test p4=new Test();
		//System.out.println(p.getInfo());
	}
	
}
结果:
生成了对象数量1
生成了对象数量2
生成了对象数量3
生成了对象数量4
代码块
class Test{
	{
		System.out.println("构造块");
	}
	static {
		System.out.println("静态代码块");
	}
	public Test() {
		System.out.println("构造方法");
	}
	public String getInfo() {
		//this();   //只能放在构造方法中并且是首行
		return "姓名"+"年龄";
	}
	public static void main(String[] args) {
		Test p=new Test();
		Test p2=new Test();
		Test p3=new Test();
		Test p4=new Test();
		//System.out.println(p.getInfo());
	}
	
}
结果:
静态代码块
构造块
构造方法
构造块
构造方法
构造块
构造方法
构造块
构造方法

从结果可以看出来不管有多少个对象产生(本例子是四个对象)静态代码块只初始化一次。
构造方法私有化
封装不仅仅体现在属性的封装

class Test{
	private int num;
	private String name;
	public int getNum() {
		return num;
	}
	public void setNum(int num) {
		this.num = num;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
}
实际的方法也可以封装,这就包括了对构造方法的封装
class Test{
	private Test() {
		System.out.println("私有化构造方法...");
	}
	public void export() {
		// TODO Auto-generated method stub
		System.out.println("我是打印方法...");
	}
	
}
一个类要想使用,必须实例化对象产生,现在想要使用Test中的方法export,有两种方式
1.内部实例化Test对象,并设置为static静态属性
2.通过静态方法取得Test对象
class Test{
	
	static Test ts=new Test();  //1.static共享内部对象方式
	
	private static Test instacce=new Test();
	private Test() {
		System.out.println("私有化构造方法...");
	}
	public static Test getInstacce() {
		return instacce;
	}
	public void export() {
		// TODO Auto-generated method stub
		System.out.println("我是打印方法...");
	}
	
}

public class Test2 {
	
	public static void main(String[] args) {
		//1
		/*Test t1 = null;
		Test tt=t1.ts;
		tt.export();*/
		//2
		Test t1 = null;
		Test tt=t1.getInstacce();
		tt.export();
	}
}
对象数组
使用对象数组一定要清楚一点,数组一定要先开辟空间,但是因为其是引用数据类型,所以数组中每一个对象都
是null值,则在使用对象数组中每一个对象必须分别进行实例化操作
class Test{
	
	String name;
	public Test() {
		System.out.println("私有化构造方法...");
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

public class Test2 {
	
	public static void main(String[] args) {
		Test ts[]=new Test[3];
		//现在ts[]的值全是null
		
		ts[0]=new Test("zhang");
		ts[1]=new Test("qian");
		ts[2]=new Test("suan");
		//现在ts[]数组的值就是上面赋予的值。
		//要使用ts[0].getName()方式调用。
		
	}
}
内部类

类的内部可以定义成员变量和方法,而且在类的内部也可以定义另一个类,如果在类Outer的内部再定义一个类Inner,此时类Inner就称为内部类,而Outer则被称为外部类。
内部类可以声明public或者private,访问权限和普通的成员变量和方法完全相同。

这是一个内部类
class Outer{
	private String info="Holle World!";
	class Inner{
		public void print() {
			System.out.println(info);
		}
	}
	public void fun() {
		new Inner().print();
	}
	public static void main(String[] args) {
		new Outer().fun();
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值