- 构建字符串
- 有些时候,需要由较短的字符串构建字符串,例如,按键或来自文件中的单词。采用字符串连接的方式达到此目的效率比较低。每次连接字符串,都会构建一个新的String对象,即耗时,又浪费空间。使用StringBuilder可以避免这个问题发生。
- 如果需要用许多小段构建一个字符串,按以下步骤:
- 首先构建一个空的字符串构建器
- StringBuidler builder = new StringBuilder();
- 当每次需要添加一部分内容时,就调用append方法
- builder.append(ch); //appends a single character
- builder.append(str); //appends a string
- 在需要构建字符串时就调用toString方法,将可以得到一个String对象,其中包含了构建器中的字符序列
- String completedString = builder.toString();
- 首先构建一个空的字符串构建器
- for each 增强循环
- 可以用来依次处理数组中的每个元素(其他类型的元素集合亦可),而不必为指定下标值儿分心,其语句格式为
- for(variable : collection) statement
- 定义一个变量用于暂存集合中的每一个元素,并执行相应的语句(当然,也可以是语句块)。collection这一集合表达式必须是一个数组或者是一个实现了Iterable接口的类对象(例如ArraList)
- 可以用来依次处理数组中的每个元素(其他类型的元素集合亦可),而不必为指定下标值儿分心,其语句格式为
- 多态:在Java程序设计语言中,对象变量是多态的。一个Employee变量既可以引用一个Employee类对象,也可以引用一个Employee类的任何一个子类的对象(例如Manage等)
- 阻止继承:final类和方法
- 有时候,可能希望阻止人们利用某个类定义子类,不允许扩展的类被称为final类。如果在定义时使用了final修饰符就表明这个类是final
- 类中的特定方法也可以被声明为final。如果这样做,子类就不能覆盖这个方法(final类中所有的方法自动的成为final方法)
- Object:所有类的超类
- Object类是Java中所有类的始祖,在Java中每个类都是由它扩展而来。
- 可以使用Object类型的变量引用任何类型的对象,如:
- Object obj = new Employee("Harry",3500);
- 枚举类
- 如:public enum Size {SMALL,MEDIUM,LARGE,EXTRA_LARGE}
- 实际上,这个声明定义的类型是一个类,它刚好有4个实例,在此尽量不好构造新对象。因此,在比较两个枚举类型的值,永远不需要调用equals,而直接使用“==”就可以了。
- 所有的枚举类型是Enum类的子类。
- 反射
- 能够分析类能力的程序称为反射(reflective)。反射机制可以用来:
- 在运行时分析类的能力
- 在运行时查看对象,例如,编写一个toString方法供所有类使用
- 实现通用的数组操作代码
- 利用Method对象,这个对象很像C++中的函数指针
- 反射是一种功能强大且复杂的机制。使用它的主要人员是工具的构造者,而不是应用程序员。
- 在java.lang.reflect包中有三个类Fiedl、Method和Constructor分别用于描述类的域、方法和构造器。这三个类都有一个叫做getName的方法,用来返回项目的名称。Field类有一个getType方法,用来返回描述域所属类型的Class对象。Method和Constructor类有能报告参数类型的方法,Method还有一个可以报告返回类型的方法。
- Class类中的getFields、getMethods和getConstructor方法将分别返回类提供的域、方法和构造器数组。
- 虽然前途未卜,我愿陪你吃土
- 能够分析类能力的程序称为反射(reflective)。反射机制可以用来:
- 接口
- 接口不是类,尤其不能使用new运算符实例化一个接口。尽管不能构造接口的对象,却能声明接口的变量。
- 在接口中不能包含实例域或静态方法,但可以包含常量。
- 接口与回调
- 回调是一种常见的程序设计模式,在这种模式中,可以指出某个特定事件发生时应该采取的动作。
- lambda表达式
- lambda表达式是一个可传递的代码块,以及必须传入代码的变量规范,可以在以后执行一次或多次。
- Java中的lambda表达式:参数:箭头(->)以及一个表达式。例如:
- 定时器事件
- Timer t = new Timer(1000,event ->System.out.println("The time is"+new Date()));
- t.start();
- 函数式接口
- 对于只有一个抽象方法的接口,需要这种接口的对象时,就可以提供一个lambda表达式。这种接口称为函数式接口。
- 方法引用
- 有时,可能已经有现成的方法可以完成你想要传递到其他代码的某个动作。例如,假设你希望只要出现一个定时器事件就打印这个事件的对象。当然,为此也可以调用:
- Timer t = new Time(1000,event -> System.out.pintln(event));
- 但是直接把println方法传递到Timer构造器就更好了。具体做法如下:
- Timer t = new Time(1000, System.out :: pintln);
- 表达式System.out :: pintln是一个方法引用,等价于lambda表达式x -> System.out.println(x)
- 有时,可能已经有现成的方法可以完成你想要传递到其他代码的某个动作。例如,假设你希望只要出现一个定时器事件就打印这个事件的对象。当然,为此也可以调用:
- lambda表达式中捕获的变量必须实际上是最终变量。实际上的最终变量是指,这个变量初始化之后就不会再为他赋新值。
- lambda表达式的重点是延迟执行。
- 内部类
- 内部类是定义在另一个类中的类
- 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据。
- 内部类可以对同一个包中的其他类隐藏起来。
- 当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷
- 内部类中声明的所有静态域都必须是final。
- 内部类不能有static方法。
- 局部内部类
- 当有些内部类只在方法中被创建过对象只使用了一次时,可以在这个方法中定义局部内部类
- 局部类不能用 public 或 private访问说明符进行声明。它的作用域被限定在声明这个局部 类的块中。 局部类有一个优势, 即对外部世界可以完全地隐藏起来。 即使 TalkingClock 类中的其他 代码也不能访问它。除 start 方法之外, 没有任何方法知道 TimePrinter 类的存在。
- 与其他局部类相比较,局部类还有一个优点。他们不仅能够访问包含他们的外部类,还可以访问局部变量。不过,那些局部变量必须事实上为final。这说明,他们一旦赋值就绝不会改变。
- 匿名内部类
- 将局部内部类的使用再深入一步,假如只创建这个类的一个对象,就不必命名了,这种类被称为匿名内部类。
- public void start( int interval,boolean beep)
- ActionListener listener=new ActionListener()
- {
- public void actionPerformed(ActionEvent event)
- {
- System.out.println("At the tone,the time is"+new date());
- if(beep) Toolkit.getDefaultToolkit().beep();
- };
- Timer t=new Timer(interval,listener);
- t.start();
- }
- public void start( int interval,boolean beep)
- 语法含义是,创建一个ActionListener接口的类的的新对象,需要实现的方法actionPerformed定义在括号{}内.
- 通常语法格式为:
- new SuperType(construction parameters)
- {
- inner class method and data
- }
- {
- new SuperType(construction parameters)
- 由于构造器的名字必须与类名相同,而匿名类没有类名,所以匿名类不能有构造器。取而代之的是,将构造器参数传递给超类构造器。尤其是在内部类实现接口的时候,不能有任何构造参数。
- 将局部内部类的使用再深入一步,假如只创建这个类的一个对象,就不必命名了,这种类被称为匿名内部类。
- 静态内部类
- 有时候,使用内部类只是为了把一个类隐藏在另外一个类的内部,并不需要内部类引用外围类对象。为此,可以将内部类声明为static,以便取消产生的引用
- 代理
- 代理可以在运行时创建一个实现了一组给定接口的新类。这种功能只有在编译时无法确定需要实现哪个接口时才有必要使用。
- 何时使用代理
- 假设有一个表示接口的Class对象(有可能只包含一个接口),它的确切类型在编译时无法知道。这确实有些难度。要想构造一个实现这些接口的类,就需要使用newInstance方法或反射找出这个类的构造器。但是不能实例化一个接口,需要在程序处于运行状态时定义一个新类。而代理类可以在运行时创建全新的类。这样的代理类能够实现指定的接口。尤其是,它具有下列方法:
- 指定接口所需的全部方法
- Object中的全部方法,例如,toString、equals等
- 假设有一个表示接口的Class对象(有可能只包含一个接口),它的确切类型在编译时无法知道。这确实有些难度。要想构造一个实现这些接口的类,就需要使用newInstance方法或反射找出这个类的构造器。但是不能实例化一个接口,需要在程序处于运行状态时定义一个新类。而代理类可以在运行时创建全新的类。这样的代理类能够实现指定的接口。尤其是,它具有下列方法:
- 创建代理对象
- 要想创建一个代理对象,需要使用Proxy类的newProxyInstance方法,这方法有三个参数:
- 一个类加载器
- 一个Class对象数组,每个元素都需要实现的接口
- 一个调用处理器
- 代理类都扩展与Proxy类,一个代理类只有一个实例域--调用处理器,它定义在Proxy超类中
- 代理类一定是public和final
- 要想创建一个代理对象,需要使用Proxy类的newProxyInstance方法,这方法有三个参数:
- 高级日志
- 调用getLogger方法创建或获取记录器:
- public static final Logger myLogger = Logger.getLogger("com.mycompany.myapp");
- 未被任何变量引用的日志记录器可能会被垃圾回收。为了防止这种情况发生,要向上面的例子一样,用一个静态变量存储日志记录器的一个引用。
- 调用getLogger方法创建或获取记录器:
- 程序调试小技巧
- 一个不太为人知但却非常有效的技巧是在每个类中放置一个单独的main方法。这样就可以对每个类进行单元测试。
- public class MyClass
- }
- {
- merhods and fields
- ....
- public static void main(String[] args)
- {
- test code
- }
- public class MyClass
- 利用这种技巧,只需创建少量的对象,调用所有的方法,并检测每个方法是否能够正确的运行就可以了。另外, 可以为每个类保留一个 main方法,然后分别为每个文件调用 Java 虚拟机进行运行测试。在运行 applet 应用程序的时候, 这些 main方法不会被调用,而 在运行应用程序的时候,Java 虚拟机只调用启动类的 main方法。
- 一个不太为人知但却非常有效的技巧是在每个类中放置一个单独的main方法。这样就可以对每个类进行单元测试。
- 泛型程序设计
- 定义简单泛型类
- 一个泛型类就是一个或多个类型变量的类。例如
- public class Pair<T>
- {
- private T first;
- private T second;
- public Pair(){first = null;second = null;}
- public Pair(T first, T second){this.first=first;this.second=second;}
- public T getFirst(){return first;}
- public T getSecond(){return second;}
- public void setFirst(T newValue){first=newValue;}
- public void setSecond(T newValue){second=newValue;}
- }
- {
- Pair类引入了一个类型变量T,用尖括号(<>)括起来。泛型类可以有多个类型变量。例如,可以定义Pair类,其中第一个域和第二个域使用不同类型:
- public class Pair<T,U>{.....}
- public class Pair<T>
- 一个泛型类就是一个或多个类型变量的类。例如
- 泛型方法
- class ArrayAlg
- {
- public static <T> T getMiddle(T....a)
- {
- return a[a.length/2]
- }
- }
- 类型变量放在修饰符的后面,返回类型的前面。泛型方法可以定义在普通类中,也可以定义在泛型类中。
- 当调用一个泛型方法时,在方法名前的尖括号中放入具体类型,如
- String middle = ArrayAlg.<String>getMiddle("John","Q","Public");
- 类型变量的限定
- 有时,类或方法需要对类型变量加以约束。如下例子
- class ArrayAIg t
- {
- public static <T> T iin(T[] a) // almost correct
- { if (a null || a.length = 0)
- return null;
- T smallest = a[0];
- for (int i = 1 ; i < a.length; i++)
- if (smallest.compareTo(a[i]) > 0)
- smallest = a[i];
- return smallest;
- }
- }
- public static <T> T iin(T[] a) // almost correct
- {
- class ArrayAIg t
- 但是,在main方法的内部,变量smallest类型为T,这意味着它可以时任何一个类的对象,T所属的类不一定有compareTo方法,调用时会产生一个编译错误,解决这个问题的方案是将T限定为实现了Comparable接口(只含有一个方法compareTo的标准接口)的类。可以通过对类型变量T设置限定(bound)实现这一点:
- public static <T extends Comparable> T main(T[] a)....
- 一个类型变量或通配符可以有多个限定,例如
- T extends Comparable & Serializable
- 限定类型用“ &” 分隔,而逗号用来分隔类型变量。
- 有时,类或方法需要对类型变量加以约束。如下例子
- 通配符类型
- 通配符类型中允许类型参数变化,例如,通配符类型
- Pair<? extends Enployee>
- 表示泛型Pair类型。它的类型参数时Enployee的子类
- 通配符类型中允许类型参数变化,例如,通配符类型
- 定义简单泛型类
- 集合
- Collection接口
- 在Java类库中,集合类的基本接口时Collection
- 迭代器
- Iterator接口包含四个方法:
- public interface Iterator<E>
- {
- E next();
- boolean hasNext();
- void remove();
- default void forEachRemaining(Consuter<? super E> action)
- }
- 通过反复调用next方法,可以逐个访问集合中的每个元素。但是,如果到达了集合的末尾,next方法将抛出一个NoSuchElementException。因此,需要在调用hasNext方法。
- Iterator接口包含四个方法:
- 集合中的接口
- 集合中有两个基本接口:Collection和Map
- List是一个有序集合。元素会增加到容器中的特定位置。可以采用两种方式访问元素:使用迭代器访问,或者使用一个整数索引来访问。后一种方法称为随机访问,因为这样可按任意顺序访问元素。与之不同,使用迭代器访问时,必须顺序的访问元素
- Set接口等同于Collection接口,不过其方法的行为有更严谨的定义。集(set)的add方法不允许增加重复元素。要适当地定义集的 equals方法:只要两个集包含同样的元素就认 为是相等的,而不要求这些元素有同样的顺序。hashCode方法的定义要保证包含相同元素的 两个集会得到相同的散列码。
- ArrayList 一种可以动态增长和缩减的索引序列
- LinkedList 一种可以在任何位置进行高效的插入和删除操作的有序序列
- HashSet 一种没有重复元素的无序集合
- TreeSet 一种有序集
- HashMap 一种存储键/值关联的数据结构
- TreeMap 一种键值有序排列的映射表
- 在之前我们使用了数组以及ArrayList类,然而数组和数组列表都有一个重大缺陷,就是从数组的中间删除一个元素要付出很大的代价,其原因是数组中处于被删除之后的所有元素都要向数组的前端移动
- 链表将每个对象存放在独立的结点中,每个结点还存放着序列中下个结点的引用和指向前驱结点的引用。
- 链表是个有序集合
- 散列表(hash table)可以快速的查找所需要的对象。
- 树集(treeset),是一个有序集,可以以任意顺序将元素插入到集合中。在对集合进行遍历时,每个值都自动的按照排序后的顺序呈现。
- 集合中有两个基本接口:Collection和Map
- 映射
- Java类库为映射提供了两个通用实现:HashMap和TreeMap。这两个类都实现了Map接口。散列映射对键进行散列,树映射用键的整体顺序对元素进行排序
- 键必须是唯一的,不能对同一个键存放两个值。如果对同一个键两次调用put方法,第二个值就会取代第一个值。
- 图形设计
- 在Java中,顶层窗口(就是没有包含在其他窗口中的窗口)被称为框架。在AWT(抽象窗口工具箱)有一个称为Frame的类,用于描述顶层窗口,这个类的Swing版本名为JFrame,它扩展于Frame.
- 坐标(0,0)位于屏幕左上角
- 如果没有明确指定框架的大小,所有框架的默认值为0*0像素。对于专业的应用程序来说,应该检查屏幕的分辨率,并根据分辨率编写代码重置框架大小
- 并发
- 多线程程序在较低的层次上扩展了多任务的概念:一个程序执行多个任务。通常,每一个任务称为一个线程,它是线程控制的简称。可以同时运行一个以上线程的程序称为多线程程序。
- 多进程和多线程的本质区别在于每个进程拥有自己的一整套变量,而线程则共享数据,而共享变量使线程之间的通信比进程之间的通信更有效、更容易。
- 不要调用Tread类或Runnable对象的run方法。直接调用run方法,只会执行同一个线程中的任务,而不会启动新线程。应该调用Tread.start方法。这个方法将创建一个执行run方法的新线程。
- Thread(Runnable target) 构造一个新线程,用于调用给定目标的run()方法
- void start() 启动这个线程,将引发调用run()方法。这个方法将立即返回,并且新线程将并发运行
- void run() 必须覆盖这个方法,并在这个方法中提供座所要执行的任务指令
- 中断线程
- 当线程的run方法执行方法体中最后一条语句后,并由执行return语句返回时,或者出现了在方法中没有捕获异常时,线程将终止。
- 没有可以强制线程终止的方法,然而,Interrupt方法可以用来请求终止线程,当调用这方法时,线程的终断状态将会被置位。这是每个线程都具有的boolean标志,每个线程都应该不时的检查这个标志,以判断线程是否被中断。
- 要弄清楚中断状态是否被置位,首先调用静态的Tread.currentThread方法获得当前线程,然后调用isinterrupted方法
- while(!Tread.currentThread.isinterrupted()&& more work to do)
- {
- do more work
- }
- {
- 线程被阻塞(调用sleep或wait)时,就无法检测中断状态,如果此时调用interrupt方法将会产生异常。
- 线程优先级
- 在Java程序设计语言中,每个线程有一个优先级。可以用setPriority方法提高或者降低一个线程的优先级
- 线程优先级高度依赖于系统,Windows有7个优先级,但在Oracle为Linux提供的虚拟机中,线程的优先级被忽略-所有线程具有相同的优先级。
- 线程最小优先级为1,默认优先级为5,最大优先级为10
- 守护线程
- 可以通过调用t.setDaemon(true);将线程转换为守护线程。
- 守护线程的唯一用途是为其他线程提供服务,计时线程就是个很好的例子,它定时的发送“计时器滴答”信号给其他线程或清空过时的高速缓存项的线程,当只剩下守护线程时,虚拟机就退出
- void setDaemon(boolean isDaemon),标识该线程为守护线程或用户线程,这一方法必须在线程启动之前调用。
- 同步
- 我知道我明了我想要你快乐
- 竞争条件
- 锁对象
- 有两种机制防止代码块受并发访问的干扰。Java语言提供一个synchronized关键字达到这一目的,并且Java SE 5.0引入了ReentranLock类。
- 用ReentratLock保护代码块的基本结构如下:
- myLock.lock();// a ReentratLock object
- try
- {
- critical section
- }
- finally
- {myLock.unlock;// make sure the lock is unlocked even if an exception is thrown
- }
- 这一结构确保任何时刻只有一个线程进入临界区。一旦一个线程封锁了锁对象,其他任何线程都无法通过lock语句。当其他线程调用lock时,他们被阻塞,直到第一个线程释放锁对象。
- 把解锁操作括在finally子句之内是至关重要的。如果在临界区的代码抛出异常,锁必须被释放。否则,其他线程将永远阻塞
- 如果使用锁就不能使用带资源的try语句。
- 线程在每一次调用lock都要调用unlock来释放锁。由于这一特性,被一个锁保护的的代码可以调用另一个使用相同的锁的方法
- 锁用来保护代码片段,任何时刻只能有一个线程执行被保护的代码
- 锁可以管理试图进入被保护代码片段的线程
- 锁可以拥有一个或多个相关的条件对象
- 每个条件对象管理那些已经进入被保护的代码段但还不能运行的线程
- synchronized关键字
- 如果一个方法运用了synchronized关键字声明,那么对象锁将保护整个方法。也就是说,要调用该方法,线程必须获得内部的对象锁。
- 即
- public synchronized void method()
- {
- method body
- }
- 等价于
- public void method()
- {
- this.intrinsicLock.lock();
- try
- {
- method body
- }
- finally{this.intrinsicLock();}
- }
- 内部对象锁只有一个相关条件,wait方法添加一个线程到等待集中,notifyAll/notify方法解除等待线程的阻塞状态。
- 阻塞队列
- 对于许多线程问题,可以通过使用一个或多个队列以优雅且安全的方式将其形式化。使用队列,可以安全的从一个线程向另一个线程传递数据
- 当试图向队列添加元素而队列已满,或者想从队列移出元素而队列为空的时候,阻塞队列导致线程阻塞。
- Runnable封装一个异步运行任务,可以把它想象为一个没有参数和返回值的异步方法。Callable于Runnable类似,但有返回值。Callable接口是一个参数化类型,只有一个方法call。
- 图形设计
- Collection接口
Java基础知识了解一下
最新推荐文章于 2022-05-04 20:21:13 发布